summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am2
-rw-r--r--babeld/babeld.c3
-rw-r--r--babeld/message.c15
-rw-r--r--babeld/route.c13
-rw-r--r--bfdd/bfd.c43
-rw-r--r--bfdd/bfd.h14
-rw-r--r--bfdd/bfd_packet.c2
-rw-r--r--bfdd/bfdctl.h10
-rw-r--r--bfdd/bfdd_cli.c106
-rw-r--r--bfdd/bfdd_nb.c20
-rw-r--r--bfdd/bfdd_nb.h10
-rw-r--r--bfdd/bfdd_nb_config.c83
-rw-r--r--bfdd/bfdd_vty.c34
-rw-r--r--bfdd/bfddp_packet.h5
-rw-r--r--bfdd/config.c26
-rw-r--r--bfdd/dplane.c3
-rw-r--r--bfdd/ptm_adapter.c3
-rw-r--r--bgpd/bgp_evpn_mh.c36
-rw-r--r--bgpd/bgp_evpn_mh.h1
-rw-r--r--bgpd/bgp_evpn_vty.c7
-rw-r--r--bgpd/bgp_io.c47
-rw-r--r--bgpd/bgp_packet.c34
-rw-r--r--bgpd/bgp_packet.h4
-rw-r--r--bgpd/bgp_route.c8
-rw-r--r--bgpd/bgp_vty.c4
-rw-r--r--bgpd/bgp_zebra.c2
-rw-r--r--bgpd/bgpd.c2
-rwxr-xr-xconfigure.ac22
-rw-r--r--debian/frr.install1
-rw-r--r--doc/developer/cross-compiling.rst2
-rw-r--r--doc/developer/images/PCEPlib_design.jpgbin0 -> 42003 bytes
-rw-r--r--doc/developer/images/PCEPlib_internal_deps.jpgbin0 -> 31742 bytes
-rw-r--r--doc/developer/images/PCEPlib_socket_comm.jpgbin0 -> 36823 bytes
-rw-r--r--doc/developer/images/PCEPlib_threading_model.jpgbin0 -> 69181 bytes
-rw-r--r--doc/developer/images/PCEPlib_threading_model_frr_infra.jpgbin0 -> 83409 bytes
-rw-r--r--doc/developer/images/PCEPlib_timers.jpgbin0 -> 37363 bytes
-rw-r--r--doc/developer/index.rst1
-rw-r--r--doc/developer/logging.rst4
-rw-r--r--doc/developer/pceplib.rst781
-rw-r--r--doc/developer/scripting.rst2
-rw-r--r--doc/developer/subdir.am1
-rw-r--r--doc/developer/topotests.rst18
-rw-r--r--doc/user/bfd.rst26
-rw-r--r--eigrpd/eigrp_cli.c27
-rw-r--r--eigrpd/eigrp_packet.c12
-rw-r--r--eigrpd/eigrp_zebra.c4
-rw-r--r--isisd/isis_adjacency.c17
-rw-r--r--isisd/isis_adjacency.h2
-rw-r--r--isisd/isis_circuit.c74
-rw-r--r--isisd/isis_circuit.h9
-rw-r--r--isisd/isis_dr.c1
-rw-r--r--isisd/isis_dynhn.c35
-rw-r--r--isisd/isis_dynhn.h3
-rw-r--r--isisd/isis_lsp.c8
-rw-r--r--isisd/isis_nb.h89
-rw-r--r--isisd/isis_nb_notifications.c136
-rw-r--r--isisd/isis_pdu.c180
-rw-r--r--isisd/isis_snmp.c3457
-rw-r--r--isisd/isis_spf.c16
-rw-r--r--isisd/isisd.c13
-rw-r--r--isisd/isisd.h18
-rw-r--r--isisd/subdir.am10
-rw-r--r--ldpd/lde.c12
-rw-r--r--ldpd/ldp_snmp.c1244
-rw-r--r--ldpd/ldpd.c29
-rw-r--r--ldpd/ldpd.h28
-rw-r--r--ldpd/ldpe.c20
-rw-r--r--ldpd/ldpe.h5
-rw-r--r--ldpd/neighbor.c31
-rw-r--r--ldpd/notification.c79
-rw-r--r--ldpd/packet.c10
-rw-r--r--ldpd/subdir.am9
-rw-r--r--lib/agentx.c10
-rw-r--r--lib/filter_nb.c368
-rw-r--r--lib/northbound.c10
-rw-r--r--lib/plist.c1
-rw-r--r--lib/smux.h4
-rw-r--r--lib/vty.c28
-rw-r--r--lib/zclient.c2
-rw-r--r--nhrpd/nhrp_interface.c12
-rw-r--r--nhrpd/nhrp_nhs.c14
-rw-r--r--nhrpd/nhrp_peer.c6
-rw-r--r--nhrpd/nhrpd.h4
-rw-r--r--nhrpd/vici.c21
-rw-r--r--ospf6d/ospf6_asbr.c7
-rw-r--r--ospf6d/ospf6_message.c84
-rw-r--r--ospf6d/ospf6_zebra.c10
-rw-r--r--pathd/path_pcep.c25
-rw-r--r--pathd/path_pcep.h4
-rw-r--r--pathd/path_pcep_cli.c22
-rw-r--r--pathd/path_pcep_config.c25
-rw-r--r--pathd/path_pcep_config.h9
-rw-r--r--pathd/path_pcep_controller.c227
-rw-r--r--pathd/path_pcep_controller.h22
-rw-r--r--pathd/path_pcep_debug.c6
-rw-r--r--pathd/path_pcep_debug.h4
-rw-r--r--pathd/path_pcep_lib.c14
-rw-r--r--pathd/path_pcep_lib.h2
-rw-r--r--pathd/path_pcep_pcc.c126
-rw-r--r--pathd/path_pcep_pcc.h9
-rw-r--r--pathd/pathd.c9
-rw-r--r--pathd/subdir.am13
-rw-r--r--pceplib/.gitignore14
-rw-r--r--pceplib/pcep.h48
-rw-r--r--pceplib/pcep_msg_encoding.h140
-rw-r--r--pceplib/pcep_msg_messages.c308
-rw-r--r--pceplib/pcep_msg_messages.h132
-rw-r--r--pceplib/pcep_msg_messages_encoding.c351
-rw-r--r--pceplib/pcep_msg_object_error_types.c389
-rw-r--r--pceplib/pcep_msg_object_error_types.h284
-rw-r--r--pceplib/pcep_msg_objects.c854
-rw-r--r--pceplib/pcep_msg_objects.h741
-rw-r--r--pceplib/pcep_msg_objects_encoding.c1720
-rw-r--r--pceplib/pcep_msg_tlvs.c464
-rw-r--r--pceplib/pcep_msg_tlvs.h380
-rw-r--r--pceplib/pcep_msg_tlvs_encoding.c1282
-rw-r--r--pceplib/pcep_msg_tools.c465
-rw-r--r--pceplib/pcep_msg_tools.h71
-rw-r--r--pceplib/pcep_pcc.c517
-rw-r--r--pceplib/pcep_pcc_api.c392
-rw-r--r--pceplib/pcep_pcc_api.h103
-rw-r--r--pceplib/pcep_session_logic.c683
-rw-r--r--pceplib/pcep_session_logic.h290
-rw-r--r--pceplib/pcep_session_logic_counters.c450
-rw-r--r--pceplib/pcep_session_logic_internals.h109
-rw-r--r--pceplib/pcep_session_logic_loop.c360
-rw-r--r--pceplib/pcep_session_logic_states.c1132
-rw-r--r--pceplib/pcep_socket_comm.c781
-rw-r--r--pceplib/pcep_socket_comm.h198
-rw-r--r--pceplib/pcep_socket_comm_internals.h69
-rw-r--r--pceplib/pcep_socket_comm_loop.c486
-rw-r--r--pceplib/pcep_socket_comm_loop.h32
-rw-r--r--pceplib/pcep_socket_comm_mock.c363
-rw-r--r--pceplib/pcep_socket_comm_mock.h67
-rw-r--r--pceplib/pcep_timer_internals.h76
-rw-r--r--pceplib/pcep_timers.c482
-rw-r--r--pceplib/pcep_timers.h92
-rw-r--r--pceplib/pcep_timers_event_loop.c106
-rw-r--r--pceplib/pcep_timers_event_loop.h34
-rw-r--r--pceplib/pcep_utils_counters.c475
-rw-r--r--pceplib/pcep_utils_counters.h232
-rw-r--r--pceplib/pcep_utils_double_linked_list.c262
-rw-r--r--pceplib/pcep_utils_double_linked_list.h72
-rw-r--r--pceplib/pcep_utils_logging.c82
-rw-r--r--pceplib/pcep_utils_logging.h66
-rw-r--r--pceplib/pcep_utils_memory.c220
-rw-r--r--pceplib/pcep_utils_memory.h89
-rw-r--r--pceplib/pcep_utils_ordered_list.c322
-rw-r--r--pceplib/pcep_utils_ordered_list.h109
-rw-r--r--pceplib/pcep_utils_queue.c150
-rw-r--r--pceplib/pcep_utils_queue.h49
-rw-r--r--pceplib/subdir.am62
-rw-r--r--pceplib/test/pcep_msg_messages_test.c498
-rw-r--r--pceplib/test/pcep_msg_messages_test.h48
-rw-r--r--pceplib/test/pcep_msg_messages_tests.c256
-rw-r--r--pceplib/test/pcep_msg_object_error_types_test.c84
-rw-r--r--pceplib/test/pcep_msg_object_error_types_test.h37
-rw-r--r--pceplib/test/pcep_msg_objects_test.c1289
-rw-r--r--pceplib/test/pcep_msg_objects_test.h64
-rwxr-xr-xpceplib/test/pcep_msg_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_msg_tlvs_test.c671
-rw-r--r--pceplib/test/pcep_msg_tlvs_test.h51
-rw-r--r--pceplib/test/pcep_msg_tools_test.c1258
-rw-r--r--pceplib/test/pcep_msg_tools_test.h48
-rw-r--r--pceplib/test/pcep_pcc_api_test.c285
-rw-r--r--pceplib/test/pcep_pcc_api_test.h43
-rw-r--r--pceplib/test/pcep_pcc_api_tests.c88
-rwxr-xr-xpceplib/test/pcep_pcc_api_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_session_logic_loop_test.c219
-rw-r--r--pceplib/test/pcep_session_logic_loop_test.h40
-rw-r--r--pceplib/test/pcep_session_logic_states_test.c919
-rw-r--r--pceplib/test/pcep_session_logic_states_test.h52
-rw-r--r--pceplib/test/pcep_session_logic_test.c360
-rw-r--r--pceplib/test/pcep_session_logic_test.h43
-rw-r--r--pceplib/test/pcep_session_logic_tests.c201
-rwxr-xr-xpceplib/test/pcep_session_logic_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_socket_comm_loop_test.c194
-rw-r--r--pceplib/test/pcep_socket_comm_loop_test.h38
-rw-r--r--pceplib/test/pcep_socket_comm_test.c308
-rw-r--r--pceplib/test/pcep_socket_comm_test.h42
-rw-r--r--pceplib/test/pcep_socket_comm_tests.c128
-rwxr-xr-xpceplib/test/pcep_socket_comm_tests_valgrind.sh2
-rwxr-xr-xpceplib/test/pcep_tests_valgrind.sh15
-rw-r--r--pceplib/test/pcep_timers_event_loop_test.c160
-rw-r--r--pceplib/test/pcep_timers_event_loop_test.h38
-rw-r--r--pceplib/test/pcep_timers_test.c109
-rw-r--r--pceplib/test/pcep_timers_test.h40
-rw-r--r--pceplib/test/pcep_timers_tests.c113
-rwxr-xr-xpceplib/test/pcep_timers_tests_valgrind.sh2
-rw-r--r--pceplib/test/pcep_utils_counters_test.c254
-rw-r--r--pceplib/test/pcep_utils_counters_test.h43
-rw-r--r--pceplib/test/pcep_utils_double_linked_list_test.c297
-rw-r--r--pceplib/test/pcep_utils_double_linked_list_test.h38
-rw-r--r--pceplib/test/pcep_utils_memory_test.c241
-rw-r--r--pceplib/test/pcep_utils_memory_test.h33
-rw-r--r--pceplib/test/pcep_utils_ordered_list_test.c248
-rw-r--r--pceplib/test/pcep_utils_ordered_list_test.h39
-rw-r--r--pceplib/test/pcep_utils_queue_test.c157
-rw-r--r--pceplib/test/pcep_utils_queue_test.h36
-rw-r--r--pceplib/test/pcep_utils_tests.c136
-rwxr-xr-xpceplib/test/pcep_utils_tests_valgrind.sh2
-rw-r--r--pceplib/test/subdir.am122
-rw-r--r--pimd/pim_igmp_mtrace.c83
-rw-r--r--pimd/pim_msdp_socket.c8
-rw-r--r--redhat/frr.spec.in1
-rw-r--r--sharpd/sharp_zebra.c17
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json5
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json5
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json5
-rw-r--r--tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json5
-rw-r--r--tests/topotests/bfd-profiles-topo1/r1/bfdd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json3
-rw-r--r--tests/topotests/bfd-profiles-topo1/r2/bfdd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json2
-rw-r--r--tests/topotests/bfd-topo1/r2/peers.json2
-rw-r--r--tests/topotests/bfd-topo2/r1/peers.json10
-rw-r--r--tests/topotests/bfd-topo2/r2/peers.json15
-rw-r--r--tests/topotests/bfd-topo2/r3/peers.json5
-rw-r--r--tests/topotests/bfd-topo2/r4/peers.json10
-rw-r--r--tests/topotests/bfd-topo3/r1/bfd-peers.json15
-rw-r--r--tests/topotests/bfd-topo3/r2/bfd-peers.json10
-rw-r--r--tests/topotests/bfd-topo3/r3/bfd-peers.json15
-rw-r--r--tests/topotests/bfd-topo3/r4/bfd-peers.json10
-rw-r--r--tests/topotests/bfd-vrf-topo1/r2/peers.json2
-rwxr-xr-xtests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py25
-rw-r--r--tests/topotests/isis-snmp/ce3/zebra.conf12
-rw-r--r--tests/topotests/isis-snmp/r1/isisd.conf24
-rw-r--r--tests/topotests/isis-snmp/r1/ldpd.conf26
-rw-r--r--tests/topotests/isis-snmp/r1/show_ip_route.ref143
-rw-r--r--tests/topotests/isis-snmp/r1/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis-snmp/r1/snmpd.conf15
-rw-r--r--tests/topotests/isis-snmp/r1/zebra.conf24
-rw-r--r--tests/topotests/isis-snmp/r2/isisd.conf25
-rw-r--r--tests/topotests/isis-snmp/r2/ldpd.conf25
-rw-r--r--tests/topotests/isis-snmp/r2/show_ip_route.ref143
-rw-r--r--tests/topotests/isis-snmp/r2/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis-snmp/r2/snmpd.conf15
-rw-r--r--tests/topotests/isis-snmp/r2/zebra.conf24
-rw-r--r--tests/topotests/isis-snmp/r3/isisd.conf25
-rw-r--r--tests/topotests/isis-snmp/r3/ldpd.conf25
-rw-r--r--tests/topotests/isis-snmp/r3/show_ip_route.ref143
-rw-r--r--tests/topotests/isis-snmp/r3/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis-snmp/r3/snmpd.conf15
-rw-r--r--tests/topotests/isis-snmp/r3/zebra.conf28
-rw-r--r--tests/topotests/isis-snmp/r4/isisd.conf24
-rw-r--r--tests/topotests/isis-snmp/r4/ldpd.conf25
-rw-r--r--tests/topotests/isis-snmp/r4/show_ip_route.ref143
-rw-r--r--tests/topotests/isis-snmp/r4/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis-snmp/r4/snmpd.conf15
-rw-r--r--tests/topotests/isis-snmp/r4/zebra.conf24
-rw-r--r--tests/topotests/isis-snmp/r5/isisd.conf25
-rw-r--r--tests/topotests/isis-snmp/r5/ldpd.conf25
-rw-r--r--tests/topotests/isis-snmp/r5/ldpdconf25
-rw-r--r--tests/topotests/isis-snmp/r5/show_ip_route.ref143
-rw-r--r--tests/topotests/isis-snmp/r5/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis-snmp/r5/snmpd.conf15
-rw-r--r--tests/topotests/isis-snmp/r5/zebra.conf24
-rw-r--r--tests/topotests/isis-snmp/test_isis_snmp.dot114
-rwxr-xr-xtests/topotests/isis-snmp/test_isis_snmp.py369
-rw-r--r--tests/topotests/ldp-snmp/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp-snmp/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp-snmp/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp-snmp/r1/isisd.conf27
-rw-r--r--tests/topotests/ldp-snmp/r1/ldpd.conf35
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-snmp/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-snmp/r1/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-snmp/r1/snmpd.conf15
-rw-r--r--tests/topotests/ldp-snmp/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp-snmp/r2/isisd.conf28
-rw-r--r--tests/topotests/ldp-snmp/r2/ldpd.conf35
-rw-r--r--tests/topotests/ldp-snmp/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp-snmp/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-snmp/r2/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-snmp/r2/snmpd.conf15
-rw-r--r--tests/topotests/ldp-snmp/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp-snmp/r3/isisd.conf29
-rw-r--r--tests/topotests/ldp-snmp/r3/ldpd.conf27
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp-snmp/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp-snmp/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp-snmp/r3/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp-snmp/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py421
-rw-r--r--tools/etc/frr/support_bundle_commands.conf3
-rwxr-xr-xtools/frr-reload.py11
-rwxr-xr-xvtysh/extract.pl.in2
-rw-r--r--yang/frr-bfdd.yang11
-rw-r--r--yang/frr-filter.yang4
-rw-r--r--zebra/connected.c8
-rw-r--r--zebra/redistribute.c2
-rw-r--r--zebra/rt_netlink.c57
-rw-r--r--zebra/zebra_dplane.c23
-rw-r--r--zebra/zebra_evpn.c36
-rw-r--r--zebra/zebra_evpn_mac.c185
-rw-r--r--zebra/zebra_evpn_neigh.c263
-rw-r--r--zebra/zebra_evpn_neigh.h20
-rw-r--r--zebra/zebra_fpm.c6
-rw-r--r--zebra/zebra_fpm_netlink.c11
-rw-r--r--zebra/zebra_nhg.c186
-rw-r--r--zebra/zebra_nhg.h3
-rw-r--r--zebra/zebra_rib.c4
-rw-r--r--zebra/zebra_vxlan.c96
342 files changed, 37754 insertions, 1411 deletions
diff --git a/.gitignore b/.gitignore
index 97349769ad..05ca0b1140 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@
/libtool
/libtool.orig
/changelog-auto
+/test-driver
/Makefile
/Makefile.in
diff --git a/Makefile.am b/Makefile.am
index bb8e97a115..0dff83e505 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -158,6 +158,8 @@ include bfdd/subdir.am
include yang/subdir.am
include yang/libyang_plugins/subdir.am
include vrrpd/subdir.am
+include pceplib/subdir.am
+include pceplib/test/subdir.am
include pathd/subdir.am
include vtysh/subdir.am
diff --git a/babeld/babeld.c b/babeld/babeld.c
index 895ede7040..a907daf6c2 100644
--- a/babeld/babeld.c
+++ b/babeld/babeld.c
@@ -265,8 +265,7 @@ babel_get_myid(void)
return;
}
- flog_err(EC_BABEL_CONFIG,
- "Warning: couldn't find router id -- using random value.");
+ flog_err(EC_BABEL_CONFIG, "Couldn't find router id -- using random value.");
rc = read_random_bytes(myid, 8);
if(rc < 0) {
diff --git a/babeld/message.c b/babeld/message.c
index edb9806011..5c2e29d8b3 100644
--- a/babeld/message.c
+++ b/babeld/message.c
@@ -746,10 +746,9 @@ flushbuf(struct interface *ifp)
if(rc < 0)
flog_err(EC_BABEL_PACKET, "send: %s", safe_strerror(errno));
} else {
- flog_err(EC_BABEL_PACKET,
- "Warning: bucket full, dropping packet to %s.",
- ifp->name);
- }
+ flog_err(EC_BABEL_PACKET, "Bucket full, dropping packet to %s.",
+ ifp->name);
+ }
}
VALGRIND_MAKE_MEM_UNDEFINED(babel_ifp->sendbuf, babel_ifp->bufsize);
babel_ifp->buffered = 0;
@@ -1009,10 +1008,10 @@ flush_unicast(int dofree)
flog_err(EC_BABEL_PACKET, "send(unicast): %s",
safe_strerror(errno));
} else {
- flog_err(EC_BABEL_PACKET,
- "Warning: bucket full, dropping unicast packet to %s if %s.",
- format_address(unicast_neighbour->address),
- unicast_neighbour->ifp->name);
+ flog_err(EC_BABEL_PACKET,
+ "Bucket full, dropping unicast packet to %s if %s.",
+ format_address(unicast_neighbour->address),
+ unicast_neighbour->ifp->name);
}
done:
diff --git a/babeld/route.c b/babeld/route.c
index 0f6f6486f2..dfd0bfab89 100644
--- a/babeld/route.c
+++ b/babeld/route.c
@@ -399,15 +399,17 @@ install_route(struct babel_route *route)
return;
if(!route_feasible(route))
- flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route (this shouldn't happen).");
+ flog_err(EC_BABEL_ROUTE,
+ "Installing unfeasible route (this shouldn't happen).");
i = find_route_slot(route->src->prefix, route->src->plen, NULL);
assert(i >= 0 && i < route_slots);
if(routes[i] != route && routes[i]->installed) {
- flog_err(EC_BABEL_ROUTE,
- "WARNING: attempting to install duplicate route (this shouldn't happen).");
- return;
+ flog_err(
+ EC_BABEL_ROUTE,
+ "Attempting to install duplicate route (this shouldn't happen).");
+ return;
}
rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
@@ -463,7 +465,8 @@ switch_routes(struct babel_route *old, struct babel_route *new)
return;
if(!route_feasible(new))
- flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route (this shouldn't happen).");
+ flog_err(EC_BABEL_ROUTE,
+ "Switching to unfeasible route (this shouldn't happen).");
rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen,
old->nexthop, old->neigh->ifp->ifindex,
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index 499e54603f..9e5bc20a49 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -85,12 +85,13 @@ struct bfd_profile *bfd_profile_lookup(const char *name)
static void bfd_profile_set_default(struct bfd_profile *bp)
{
- bp->admin_shutdown = true;
+ bp->admin_shutdown = false;
bp->detection_multiplier = BFD_DEFDETECTMULT;
bp->echo_mode = false;
bp->passive = false;
bp->minimum_ttl = BFD_DEF_MHOP_TTL;
- bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO;
+ bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
+ bp->min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
bp->min_rx = BFD_DEFREQUIREDMINRX;
bp->min_tx = BFD_DEFDESIREDMINTX;
}
@@ -179,13 +180,19 @@ void bfd_session_apply(struct bfd_session *bs)
/* We can only apply echo options on single hop sessions. */
if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
- /* Configure remote echo if it was default. */
- if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO)
- bs->timers.required_min_echo = bp->min_echo_rx;
+ /* Configure echo timers if they were default. */
+ if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO_RX)
+ bs->timers.required_min_echo_rx = bp->min_echo_rx;
else
- bs->timers.required_min_echo =
+ bs->timers.required_min_echo_rx =
bs->peer_profile.min_echo_rx;
+ if (bs->peer_profile.min_echo_tx == BFD_DEF_DES_MIN_ECHO_TX)
+ bs->timers.desired_min_echo_tx = bp->min_echo_tx;
+ else
+ bs->timers.desired_min_echo_tx =
+ bs->peer_profile.min_echo_tx;
+
/* Toggle echo if default value. */
if (bs->peer_profile.echo_mode == false)
bfd_set_echo(bs, bp->echo_mode);
@@ -206,7 +213,7 @@ void bfd_session_apply(struct bfd_session *bs)
bfd_set_passive_mode(bs, bs->peer_profile.passive);
/* Toggle 'no shutdown' if default value. */
- if (bs->peer_profile.admin_shutdown)
+ if (bs->peer_profile.admin_shutdown == false)
bfd_set_shutdown(bs, bp->admin_shutdown);
else
bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
@@ -494,8 +501,10 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd)
void ptm_bfd_echo_start(struct bfd_session *bfd)
{
bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
- if (bfd->echo_detect_TO > 0)
+ if (bfd->echo_detect_TO > 0) {
+ bfd_echo_recvtimer_update(bfd);
ptm_bfd_echo_xmt_TO(bfd);
+ }
}
void ptm_bfd_sess_up(struct bfd_session *bfd)
@@ -700,7 +709,8 @@ struct bfd_session *bfd_session_new(void)
bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
- bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
+ bs->timers.required_min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
+ bs->timers.desired_min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
bs->detect_mult = BFD_DEFDETECTMULT;
bs->mh_ttl = BFD_DEF_MHOP_TTL;
bs->ses_state = PTM_BFD_DOWN;
@@ -769,9 +779,14 @@ static void _bfd_session_update(struct bfd_session *bs,
bs->peer_profile.detection_multiplier = bs->detect_mult;
}
- if (bpc->bpc_has_echointerval) {
- bs->timers.required_min_echo = bpc->bpc_echointerval * 1000;
- bs->peer_profile.min_echo_rx = bs->timers.required_min_echo;
+ if (bpc->bpc_has_echorecvinterval) {
+ bs->timers.required_min_echo_rx = bpc->bpc_echorecvinterval * 1000;
+ bs->peer_profile.min_echo_rx = bs->timers.required_min_echo_rx;
+ }
+
+ if (bpc->bpc_has_echotxinterval) {
+ bs->timers.desired_min_echo_tx = bpc->bpc_echotxinterval * 1000;
+ bs->peer_profile.min_echo_tx = bs->timers.desired_min_echo_tx;
}
if (bpc->bpc_has_label)
@@ -1189,10 +1204,10 @@ void bs_echo_timer_handler(struct bfd_session *bs)
* RFC 5880, Section 6.8.9.
*/
old_timer = bs->echo_xmt_TO;
- if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo)
+ if (bs->remote_timers.required_min_echo > bs->timers.desired_min_echo_tx)
bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
else
- bs->echo_xmt_TO = bs->timers.required_min_echo;
+ bs->echo_xmt_TO = bs->timers.desired_min_echo_tx;
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
|| old_timer != bs->echo_xmt_TO)
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index 7c537b40d0..503b5ca7d6 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -215,6 +215,8 @@ struct bfd_profile {
/** Echo mode (only applies to single hop). */
bool echo_mode;
+ /** Desired echo transmission interval (in microseconds). */
+ uint32_t min_echo_tx;
/** Minimum required echo receive interval (in microseconds). */
uint32_t min_echo_rx;
@@ -228,6 +230,13 @@ TAILQ_HEAD(bfdproflist, bfd_profile);
/* bfd_session shortcut label forwarding. */
struct peer_label;
+struct bfd_config_timers {
+ uint32_t desired_min_tx;
+ uint32_t required_min_rx;
+ uint32_t desired_min_echo_tx;
+ uint32_t required_min_echo_rx;
+};
+
/*
* Session state information
*/
@@ -251,7 +260,7 @@ struct bfd_session {
struct bfd_profile peer_profile;
/* Timers */
- struct bfd_timers timers;
+ struct bfd_config_timers timers;
struct bfd_timers cur_timers;
uint64_t detect_TO;
struct thread *echo_recvtimer_ev;
@@ -332,7 +341,8 @@ TAILQ_HEAD(obslist, bfd_session_observer);
#define BFD_DEFDETECTMULT 3
#define BFD_DEFDESIREDMINTX (300 * 1000) /* microseconds. */
#define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */
-#define BFD_DEF_REQ_MIN_ECHO (50 * 1000) /* microseconds. */
+#define BFD_DEF_DES_MIN_ECHO_TX (50 * 1000) /* microseconds. */
+#define BFD_DEF_REQ_MIN_ECHO_RX (50 * 1000) /* microseconds. */
#define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */
/** Minimum multi hop TTL. */
#define BFD_DEF_MHOP_TTL 254
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index 12bb52cf67..c871e2abe1 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -267,7 +267,7 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
cp.timers.required_min_rx =
htonl(bfd->cur_timers.required_min_rx);
}
- cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
+ cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx);
if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
return;
diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h
index e1cff9a31c..c4b2e80306 100644
--- a/bfdd/bfdctl.h
+++ b/bfdd/bfdctl.h
@@ -47,7 +47,8 @@ struct sockaddr_any {
#define BPC_DEF_DETECTMULTIPLIER 3
#define BPC_DEF_RECEIVEINTERVAL 300 /* milliseconds */
#define BPC_DEF_TRANSMITINTERVAL 300 /* milliseconds */
-#define BPC_DEF_ECHOINTERVAL 50 /* milliseconds */
+#define BPC_DEF_ECHORECEIVEINTERVAL 50 /* milliseconds */
+#define BPC_DEF_ECHOTRANSMITINTERVAL 50 /* milliseconds */
/* Peer status */
enum bfd_peer_status {
@@ -81,8 +82,11 @@ struct bfd_peer_cfg {
bool bpc_has_txinterval;
uint64_t bpc_txinterval;
- bool bpc_has_echointerval;
- uint64_t bpc_echointerval;
+ bool bpc_has_echorecvinterval;
+ uint64_t bpc_echorecvinterval;
+
+ bool bpc_has_echotxinterval;
+ uint64_t bpc_echotxinterval;
bool bpc_has_minimum_ttl;
uint8_t bpc_minimum_ttl;
diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c
index 5072c76aac..ba80b2363f 100644
--- a/bfdd/bfdd_cli.c
+++ b/bfdd/bfdd_cli.c
@@ -274,7 +274,7 @@ void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
if (show_defaults)
- vty_out(vty, " shutdown\n");
+ vty_out(vty, " no shutdown\n");
else
vty_out(vty, " %sshutdown\n",
yang_dnode_get_bool(dnode, NULL) ? "" : "no ");
@@ -433,6 +433,10 @@ DEFPY_YANG(
return CMD_WARNING_CONFIG_FAILED;
}
+ if (!no && !bglobal.bg_use_dplane) {
+ vty_out(vty, "%% Current implementation of echo mode works only when the peer is also FRR.\n");
+ }
+
nb_cli_enqueue_change(vty, "./echo-mode", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
@@ -451,8 +455,31 @@ void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode,
DEFPY_YANG(
bfd_peer_echo_interval, bfd_peer_echo_interval_cmd,
"echo-interval (10-60000)$interval",
- "Configure peer echo interval\n"
- "Configure peer echo interval value in milliseconds\n")
+ "Configure peer echo intervals\n"
+ "Configure peer echo rx/tx intervals value in milliseconds\n")
+{
+ char value[32];
+
+ if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
+ vty_out(vty, "%% Echo mode is only available for single hop sessions.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ snprintf(value, sizeof(value), "%ld", interval * 1000);
+ nb_cli_enqueue_change(vty, "./desired-echo-transmission-interval",
+ NB_OP_MODIFY, value);
+ nb_cli_enqueue_change(vty, "./required-echo-receive-interval",
+ NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(
+ bfd_peer_echo_transmit_interval, bfd_peer_echo_transmit_interval_cmd,
+ "echo transmit-interval (10-60000)$interval",
+ "Configure peer echo intervals\n"
+ "Configure desired transmit interval\n"
+ "Configure interval value in milliseconds\n")
{
char value[32];
@@ -468,17 +495,61 @@ DEFPY_YANG(
return nb_cli_apply_changes(vty, NULL);
}
-void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults)
+void bfd_cli_show_desired_echo_transmission_interval(struct vty *vty,
+ struct lyd_node *dnode, bool show_defaults)
+{
+ uint32_t value;
+
+ if (show_defaults)
+ vty_out(vty, " echo transmit-interval %d\n",
+ BFD_DEF_DES_MIN_ECHO_TX);
+ else {
+ value = yang_dnode_get_uint32(dnode, NULL);
+ vty_out(vty, " echo transmit-interval %u\n", value / 1000);
+ }
+}
+
+DEFPY_YANG(
+ bfd_peer_echo_receive_interval, bfd_peer_echo_receive_interval_cmd,
+ "echo receive-interval <disabled$disabled|(10-60000)$interval>",
+ "Configure peer echo intervals\n"
+ "Configure required receive interval\n"
+ "Disable echo packets receive\n"
+ "Configure interval value in milliseconds\n")
+{
+ char value[32];
+
+ if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) {
+ vty_out(vty, "%% Echo mode is only available for single hop sessions.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (disabled)
+ snprintf(value, sizeof(value), "0");
+ else
+ snprintf(value, sizeof(value), "%ld", interval * 1000);
+
+ nb_cli_enqueue_change(vty, "./required-echo-receive-interval",
+ NB_OP_MODIFY, value);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void bfd_cli_show_required_echo_receive_interval(struct vty *vty,
+ struct lyd_node *dnode, bool show_defaults)
{
uint32_t value;
if (show_defaults)
- vty_out(vty, " echo-interval %d\n",
- BFD_DEF_REQ_MIN_ECHO);
+ vty_out(vty, " echo receive-interval %d\n",
+ BFD_DEF_REQ_MIN_ECHO_RX);
else {
value = yang_dnode_get_uint32(dnode, NULL);
- vty_out(vty, " echo-interval %u\n", value / 1000);
+ if (value)
+ vty_out(vty, " echo receive-interval %u\n",
+ value / 1000);
+ else
+ vty_out(vty, " echo receive-interval disabled\n");
}
}
@@ -575,6 +646,21 @@ ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd,
"Configure peer echo interval\n"
"Configure peer echo interval value in milliseconds\n")
+ALIAS_YANG(
+ bfd_peer_echo_transmit_interval, bfd_profile_echo_transmit_interval_cmd,
+ "echo transmit-interval (10-60000)$interval",
+ "Configure peer echo intervals\n"
+ "Configure desired transmit interval\n"
+ "Configure interval value in milliseconds\n")
+
+ALIAS_YANG(
+ bfd_peer_echo_receive_interval, bfd_profile_echo_receive_interval_cmd,
+ "echo receive-interval <disabled$disabled|(10-60000)$interval>",
+ "Configure peer echo intervals\n"
+ "Configure required receive interval\n"
+ "Disable echo packets receive\n"
+ "Configure interval value in milliseconds\n")
+
DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd,
"[no] profile BFDPROF$pname",
NO_STR
@@ -632,6 +718,8 @@ bfdd_cli_init(void)
install_element(BFD_PEER_NODE, &bfd_peer_tx_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_echo_interval_cmd);
+ install_element(BFD_PEER_NODE, &bfd_peer_echo_transmit_interval_cmd);
+ install_element(BFD_PEER_NODE, &bfd_peer_echo_receive_interval_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_profile_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_passive_cmd);
install_element(BFD_PEER_NODE, &bfd_peer_minimum_ttl_cmd);
@@ -652,6 +740,8 @@ bfdd_cli_init(void)
install_element(BFD_PROFILE_NODE, &bfd_profile_shutdown_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_echo_interval_cmd);
+ install_element(BFD_PROFILE_NODE, &bfd_profile_echo_transmit_interval_cmd);
+ install_element(BFD_PROFILE_NODE, &bfd_profile_echo_receive_interval_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_passive_cmd);
install_element(BFD_PROFILE_NODE, &bfd_profile_minimum_ttl_cmd);
install_element(BFD_PROFILE_NODE, &no_bfd_profile_minimum_ttl_cmd);
diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c
index 64ba3cf811..29a9b5f2d5 100644
--- a/bfdd/bfdd_nb.c
+++ b/bfdd/bfdd_nb.c
@@ -103,8 +103,15 @@ const struct frr_yang_module_info frr_bfdd_info = {
.xpath = "/frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval",
.cbs = {
.modify = bfdd_bfd_profile_desired_echo_transmission_interval_modify,
- .cli_show = bfd_cli_show_echo_interval,
- }
+ .cli_show = bfd_cli_show_desired_echo_transmission_interval,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval",
+ .cbs = {
+ .modify = bfdd_bfd_profile_required_echo_receive_interval_modify,
+ .cli_show = bfd_cli_show_required_echo_receive_interval,
+ }
},
{
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop",
@@ -179,7 +186,14 @@ const struct frr_yang_module_info frr_bfdd_info = {
.xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval",
.cbs = {
.modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify,
- .cli_show = bfd_cli_show_echo_interval,
+ .cli_show = bfd_cli_show_desired_echo_transmission_interval,
+ }
+ },
+ {
+ .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval",
+ .cbs = {
+ .modify = bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify,
+ .cli_show = bfd_cli_show_required_echo_receive_interval,
}
},
{
diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h
index fbd557b6b1..874e98691f 100644
--- a/bfdd/bfdd_nb.h
+++ b/bfdd/bfdd_nb.h
@@ -43,6 +43,8 @@ int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args);
int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args);
int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args);
+int bfdd_bfd_profile_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args);
int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args);
const void *
@@ -71,6 +73,8 @@ int bfdd_bfd_sessions_single_hop_echo_mode_modify(
struct nb_cb_modify_args *args);
int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args);
+int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args);
struct yang_data *
bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem(
struct nb_cb_get_elem_args *args);
@@ -209,8 +213,10 @@ void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
-void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
+void bfd_cli_show_desired_echo_transmission_interval(
+ struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void bfd_cli_show_required_echo_receive_interval(
+ struct vty *vty, struct lyd_node *dnode, bool show_defaults);
void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void bfd_cli_peer_profile_show(struct vty *vty, struct lyd_node *dnode,
diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c
index c8dd5cc3f6..26bce4f357 100644
--- a/bfdd/bfdd_nb_config.c
+++ b/bfdd/bfdd_nb_config.c
@@ -483,17 +483,57 @@ int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args)
}
/*
- * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-echo-transmission-interval
+ * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval
*/
int bfdd_bfd_profile_desired_echo_transmission_interval_modify(
struct nb_cb_modify_args *args)
{
struct bfd_profile *bp;
+ uint32_t min_tx;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ min_tx = yang_dnode_get_uint32(args->dnode, NULL);
+ if (min_tx < 10000 || min_tx > 60000000)
+ return NB_ERR_VALIDATION;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ min_tx = yang_dnode_get_uint32(args->dnode, NULL);
+ bp = nb_running_get_entry(args->dnode, NULL, true);
+ if (bp->min_echo_tx == min_tx)
+ return NB_OK;
+
+ bp->min_echo_tx = min_tx;
+ bfd_profile_update(bp);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval
+ */
+int bfdd_bfd_profile_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct bfd_profile *bp;
uint32_t min_rx;
switch (args->event) {
case NB_EV_VALIDATE:
min_rx = yang_dnode_get_uint32(args->dnode, NULL);
+ if (min_rx == 0)
+ return NB_OK;
if (min_rx < 10000 || min_rx > 60000000)
return NB_ERR_VALIDATION;
break;
@@ -789,7 +829,46 @@ int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify(
case NB_EV_APPLY:
bs = nb_running_get_entry(args->dnode, NULL, true);
- if (echo_interval == bs->timers.required_min_echo)
+ if (echo_interval == bs->timers.desired_min_echo_tx)
+ return NB_OK;
+
+ bs->peer_profile.min_echo_tx = echo_interval;
+ bfd_session_apply(bs);
+ break;
+
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval
+ */
+int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify(
+ struct nb_cb_modify_args *args)
+{
+ uint32_t echo_interval = yang_dnode_get_uint32(args->dnode, NULL);
+ struct bfd_session *bs;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (echo_interval == 0)
+ return NB_OK;
+ if (echo_interval < 10000 || echo_interval > 60000000)
+ return NB_ERR_VALIDATION;
+ break;
+
+ case NB_EV_PREPARE:
+ /* NOTHING */
+ break;
+
+ case NB_EV_APPLY:
+ bs = nb_running_get_entry(args->dnode, NULL, true);
+ if (echo_interval == bs->timers.required_min_echo_rx)
return NB_OK;
bs->peer_profile.min_echo_rx = echo_interval;
diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c
index 53e23cf6c2..a03fb9f216 100644
--- a/bfdd/bfdd_vty.c
+++ b/bfdd/bfdd_vty.c
@@ -158,8 +158,16 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs)
bs->timers.required_min_rx / 1000);
vty_out(vty, "\t\t\tTransmission interval: %ums\n",
bs->timers.desired_min_tx / 1000);
- vty_out(vty, "\t\t\tEcho transmission interval: %ums\n",
- bs->timers.required_min_echo / 1000);
+ if (bs->timers.required_min_echo_rx != 0)
+ vty_out(vty, "\t\t\tEcho receive interval: %ums\n",
+ bs->timers.required_min_echo_rx / 1000);
+ else
+ vty_out(vty, "\t\t\tEcho receive interval: disabled\n");
+ if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
+ vty_out(vty, "\t\t\tEcho transmission interval: %ums\n",
+ bs->timers.desired_min_echo_tx / 1000);
+ else
+ vty_out(vty, "\t\t\tEcho transmission interval: disabled\n");
vty_out(vty, "\t\tRemote timers:\n");
vty_out(vty, "\t\t\tDetect-multiplier: %u\n",
@@ -168,8 +176,11 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs)
bs->remote_timers.required_min_rx / 1000);
vty_out(vty, "\t\t\tTransmission interval: %ums\n",
bs->remote_timers.desired_min_tx / 1000);
- vty_out(vty, "\t\t\tEcho transmission interval: %ums\n",
- bs->remote_timers.required_min_echo / 1000);
+ if (bs->remote_timers.required_min_echo != 0)
+ vty_out(vty, "\t\t\tEcho receive interval: %ums\n",
+ bs->remote_timers.required_min_echo / 1000);
+ else
+ vty_out(vty, "\t\t\tEcho receive interval: disabled\n");
vty_out(vty, "\n");
}
@@ -245,11 +256,13 @@ static struct json_object *__display_peer_json(struct bfd_session *bs)
bs->timers.required_min_rx / 1000);
json_object_int_add(jo, "transmit-interval",
bs->timers.desired_min_tx / 1000);
+ json_object_int_add(jo, "echo-receive-interval",
+ bs->timers.required_min_echo_rx / 1000);
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
- json_object_int_add(jo, "echo-interval",
- bs->timers.required_min_echo / 1000);
+ json_object_int_add(jo, "echo-transmit-interval",
+ bs->timers.desired_min_echo_tx / 1000);
else
- json_object_int_add(jo, "echo-interval", 0);
+ json_object_int_add(jo, "echo-transmit-interval", 0);
json_object_int_add(jo, "detect-multiplier", bs->detect_mult);
@@ -257,7 +270,7 @@ static struct json_object *__display_peer_json(struct bfd_session *bs)
bs->remote_timers.required_min_rx / 1000);
json_object_int_add(jo, "remote-transmit-interval",
bs->remote_timers.desired_min_tx / 1000);
- json_object_int_add(jo, "remote-echo-interval",
+ json_object_int_add(jo, "remote-echo-receive-interval",
bs->remote_timers.required_min_echo / 1000);
json_object_int_add(jo, "remote-detect-multiplier",
bs->remote_detect_mult);
@@ -840,11 +853,12 @@ static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop,
memset(bpc, 0, sizeof(*bpc));
/* Defaults */
- bpc->bpc_shutdown = true;
+ bpc->bpc_shutdown = false;
bpc->bpc_detectmultiplier = BPC_DEF_DETECTMULTIPLIER;
bpc->bpc_recvinterval = BPC_DEF_RECEIVEINTERVAL;
bpc->bpc_txinterval = BPC_DEF_TRANSMITINTERVAL;
- bpc->bpc_echointerval = BPC_DEF_ECHOINTERVAL;
+ bpc->bpc_echorecvinterval = BPC_DEF_ECHORECEIVEINTERVAL;
+ bpc->bpc_echotxinterval = BPC_DEF_ECHOTRANSMITINTERVAL;
bpc->bpc_lastevent = monotime(NULL);
/* Safety check: when no error buf is provided len must be zero. */
diff --git a/bfdd/bfddp_packet.h b/bfdd/bfddp_packet.h
index 8865baef6a..4ece94f577 100644
--- a/bfdd/bfddp_packet.h
+++ b/bfdd/bfddp_packet.h
@@ -163,6 +163,11 @@ struct bfddp_session {
*/
uint32_t min_rx;
/**
+ * Minimum desired echo transmission interval (in microseconds)
+ * without jitter.
+ */
+ uint32_t min_echo_tx;
+ /**
* Required minimum echo receive interval rate (in microseconds)
* without jitter.
*/
diff --git a/bfdd/config.c b/bfdd/config.c
index b71670f012..a97caf137e 100644
--- a/bfdd/config.c
+++ b/bfdd/config.c
@@ -135,7 +135,8 @@ static int parse_list(struct json_object *jo, enum peer_list_type plt,
bpc.bpc_detectmultiplier = BFD_DEFDETECTMULT;
bpc.bpc_recvinterval = BFD_DEFREQUIREDMINRX;
bpc.bpc_txinterval = BFD_DEFDESIREDMINTX;
- bpc.bpc_echointerval = BFD_DEF_REQ_MIN_ECHO;
+ bpc.bpc_echorecvinterval = BFD_DEF_REQ_MIN_ECHO_RX;
+ bpc.bpc_echotxinterval = BFD_DEF_DES_MIN_ECHO_TX;
switch (plt) {
case PLT_IPV4:
@@ -250,11 +251,16 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc)
bpc->bpc_has_txinterval = true;
zlog_debug(" transmit-interval: %" PRIu64,
bpc->bpc_txinterval);
- } else if (strcmp(key, "echo-interval") == 0) {
- bpc->bpc_echointerval = json_object_get_int64(jo_val);
- bpc->bpc_has_echointerval = true;
- zlog_debug(" echo-interval: %" PRIu64,
- bpc->bpc_echointerval);
+ } else if (strcmp(key, "echo-receive-interval") == 0) {
+ bpc->bpc_echorecvinterval = json_object_get_int64(jo_val);
+ bpc->bpc_has_echorecvinterval = true;
+ zlog_debug(" echo-receive-interval: %" PRIu64,
+ bpc->bpc_echorecvinterval);
+ } else if (strcmp(key, "echo-transmit-interval") == 0) {
+ bpc->bpc_echotxinterval = json_object_get_int64(jo_val);
+ bpc->bpc_has_echotxinterval = true;
+ zlog_debug(" echo-transmit-interval: %" PRIu64,
+ bpc->bpc_echotxinterval);
} else if (strcmp(key, "create-only") == 0) {
bpc->bpc_createonly = json_object_get_boolean(jo_val);
zlog_debug(" create-only: %s",
@@ -463,8 +469,10 @@ char *config_notify_config(const char *op, struct bfd_session *bs)
bs->timers.required_min_rx / 1000);
json_object_int_add(resp, "transmit-interval",
bs->timers.desired_min_tx / 1000);
- json_object_int_add(resp, "echo-interval",
- bs->timers.required_min_echo / 1000);
+ json_object_int_add(resp, "echo-receive-interval",
+ bs->timers.required_min_echo_rx / 1000);
+ json_object_int_add(resp, "echo-transmit-interval",
+ bs->timers.desired_min_echo_tx / 1000);
json_object_int_add(resp, "remote-detect-multiplier",
bs->remote_detect_mult);
@@ -472,7 +480,7 @@ char *config_notify_config(const char *op, struct bfd_session *bs)
bs->remote_timers.required_min_rx / 1000);
json_object_int_add(resp, "remote-transmit-interval",
bs->remote_timers.desired_min_tx / 1000);
- json_object_int_add(resp, "remote-echo-interval",
+ json_object_int_add(resp, "remote-echo-receive-interval",
bs->remote_timers.required_min_echo / 1000);
if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
diff --git a/bfdd/dplane.c b/bfdd/dplane.c
index 66b79f3b13..9cb0b0ea85 100644
--- a/bfdd/dplane.c
+++ b/bfdd/dplane.c
@@ -765,7 +765,8 @@ static void _bfd_dplane_session_fill(const struct bfd_session *bs,
msg->data.session.lid = htonl(bs->discrs.my_discr);
msg->data.session.min_tx = htonl(bs->timers.desired_min_tx);
msg->data.session.min_rx = htonl(bs->timers.required_min_rx);
- msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo);
+ msg->data.session.min_echo_tx = htonl(bs->timers.desired_min_echo_tx);
+ msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo_rx);
}
static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
index 0c70600f20..4135e5fb49 100644
--- a/bfdd/ptm_adapter.c
+++ b/bfdd/ptm_adapter.c
@@ -492,9 +492,6 @@ static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
"ptm-add-dest: failed to create BFD session");
return;
}
-
- /* Protocol created peers are 'no shutdown' by default. */
- bs->peer_profile.admin_shutdown = false;
} else {
/*
* BFD session was already created, we are just updating the
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index 175514f639..123c46f12f 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -3847,3 +3847,39 @@ void bgp_evpn_mh_finish(void)
XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info);
}
+
+/* This function is called when disable-ead-evi-rx knob flaps */
+void bgp_evpn_switch_ead_evi_rx(void)
+{
+ struct bgp *bgp;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node = NULL;
+ struct listnode *evi_next = NULL;
+ struct bgp_evpn_es_evi_vtep *vtep;
+ struct listnode *vtep_node = NULL;
+ struct listnode *vtep_next = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return;
+
+ /*
+ * Process all the remote es_evi_vteps and reevaluate if the es_evi_vtep
+ * is active.
+ */
+ RB_FOREACH(es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_REMOTE))
+ continue;
+
+ for (ALL_LIST_ELEMENTS(es->es_evi_list, evi_node, evi_next,
+ es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE))
+ continue;
+
+ for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list,
+ vtep_node, vtep_next, vtep))
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, vtep);
+ }
+ }
+}
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
index 818fad2eb6..e5186619ff 100644
--- a/bgpd/bgp_evpn_mh.h
+++ b/bgpd/bgp_evpn_mh.h
@@ -376,5 +376,6 @@ extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf,
extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj,
struct bgp_evpn_es *es);
extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj);
+extern void bgp_evpn_switch_ead_evi_rx(void);
#endif /* _FRR_BGP_EVPN_MH_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 73bce5df9a..b101589a79 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -3767,7 +3767,12 @@ DEFPY (bgp_evpn_ead_evi_rx_disable,
NO_STR
"Activate PE on EAD-ES even if EAD-EVI is not received\n")
{
- bgp_mh_info->ead_evi_rx = no? true :false;
+ bool ead_evi_rx = no? true :false;
+
+ if (ead_evi_rx != bgp_mh_info->ead_evi_rx) {
+ bgp_mh_info->ead_evi_rx = ead_evi_rx;
+ bgp_evpn_switch_ead_evi_rx();
+ }
return CMD_SUCCESS;
}
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
index 9a178395b8..7aa489e932 100644
--- a/bgpd/bgp_io.c
+++ b/bgpd/bgp_io.c
@@ -45,7 +45,7 @@
/* forward declarations */
static uint16_t bgp_write(struct peer *);
-static uint16_t bgp_read(struct peer *);
+static uint16_t bgp_read(struct peer *peer, int *code_p);
static int bgp_process_writes(struct thread *);
static int bgp_process_reads(struct thread *);
static bool validate_header(struct peer *);
@@ -181,6 +181,7 @@ static int bgp_process_reads(struct thread *thread)
bool fatal = false; // whether fatal error occurred
bool added_pkt = false; // whether we pushed onto ->ibuf
/* clang-format on */
+ int code;
peer = THREAD_ARG(thread);
@@ -190,7 +191,7 @@ static int bgp_process_reads(struct thread *thread)
struct frr_pthread *fpt = bgp_pth_io;
frr_with_mutex(&peer->io_mtx) {
- status = bgp_read(peer);
+ status = bgp_read(peer, &code);
}
/* error checking phase */
@@ -203,6 +204,12 @@ static int bgp_process_reads(struct thread *thread)
/* problem; tear down session */
more = false;
fatal = true;
+
+ /* Handle the error in the main pthread, include the
+ * specific state change from 'bgp_read'.
+ */
+ thread_add_event(bm->master, bgp_packet_process_error,
+ peer, code, NULL);
}
while (more) {
@@ -236,6 +243,7 @@ static int bgp_process_reads(struct thread *thread)
*/
if (ringbuf_remain(ibw) >= pktsize) {
struct stream *pkt = stream_new(pktsize);
+
assert(STREAM_WRITEABLE(pkt) == pktsize);
assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize);
stream_set_endp(pkt, pktsize);
@@ -449,7 +457,7 @@ done : {
*
* @return status flag (see top-of-file)
*/
-static uint16_t bgp_read(struct peer *peer)
+static uint16_t bgp_read(struct peer *peer, int *code_p)
{
ssize_t nbytes; // how many bytes we actually read
uint16_t status = 0;
@@ -459,43 +467,28 @@ static uint16_t bgp_read(struct peer *peer)
/* EAGAIN or EWOULDBLOCK; come back later */
if (nbytes < 0 && ERRNO_IO_RETRY(errno)) {
SET_FLAG(status, BGP_IO_TRANS_ERR);
- /* Fatal error; tear down session */
} else if (nbytes < 0) {
+ /* Fatal error; tear down session */
flog_err(EC_BGP_UPDATE_RCV,
"%s [Error] bgp_read_packet error: %s", peer->host,
safe_strerror(errno));
- if (peer->status == Established) {
- if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
- || CHECK_FLAG(peer->flags,
- PEER_FLAG_GRACEFUL_RESTART_HELPER))
- && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) {
- peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
- SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
- } else
- peer->last_reset = PEER_DOWN_CLOSE_SESSION;
- }
+ /* Handle the error in the main pthread. */
+ if (code_p)
+ *code_p = TCP_fatal_error;
- BGP_EVENT_ADD(peer, TCP_fatal_error);
SET_FLAG(status, BGP_IO_FATAL_ERR);
- /* Received EOF / TCP session closed */
+
} else if (nbytes == 0) {
+ /* Received EOF / TCP session closed */
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [Event] BGP connection closed fd %d",
peer->host, peer->fd);
- if (peer->status == Established) {
- if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
- || CHECK_FLAG(peer->flags,
- PEER_FLAG_GRACEFUL_RESTART_HELPER))
- && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) {
- peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
- SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
- } else
- peer->last_reset = PEER_DOWN_CLOSE_SESSION;
- }
+ /* Handle the error in the main pthread. */
+ if (code_p)
+ *code_p = TCP_connection_closed;
- BGP_EVENT_ADD(peer, TCP_connection_closed);
SET_FLAG(status, BGP_IO_FATAL_ERR);
}
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index ff2cc26d42..f04b89594e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -2699,3 +2699,37 @@ void bgp_send_delayed_eor(struct bgp *bgp)
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
bgp_write_proceed_actions(peer);
}
+
+/*
+ * Task callback to handle socket error encountered in the io pthread. We avoid
+ * having the io pthread try to enqueue fsm events or mess with the peer
+ * struct.
+ */
+int bgp_packet_process_error(struct thread *thread)
+{
+ struct peer *peer;
+ int code;
+
+ peer = THREAD_ARG(thread);
+ code = THREAD_VAL(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [Event] BGP error %d on fd %d",
+ peer->host, peer->fd, code);
+
+ /* Closed connection or error on the socket */
+ if (peer->status == Established) {
+ if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ || CHECK_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_HELPER))
+ && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) {
+ peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ } else
+ peer->last_reset = PEER_DOWN_CLOSE_SESSION;
+ }
+
+ bgp_event_update(peer, code);
+
+ return 0;
+}
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 525859a2da..d32f091d0c 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -83,4 +83,8 @@ extern int bgp_generate_updgrp_packets(struct thread *);
extern int bgp_process_packet(struct thread *);
extern void bgp_send_delayed_eor(struct bgp *bgp);
+
+/* Task callback to handle socket error encountered in the io pthread */
+int bgp_packet_process_error(struct thread *thread);
+
#endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 9165dcc1d7..87fd5f28ca 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -995,7 +995,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (newm < existm) {
if (debug)
zlog_debug(
- "%s: %s wins over %s due to IGP metric %d < %d",
+ "%s: %s wins over %s due to IGP metric %u < %u",
pfx_buf, new_buf, exist_buf, newm, existm);
ret = 1;
}
@@ -1003,7 +1003,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (newm > existm) {
if (debug)
zlog_debug(
- "%s: %s loses to %s due to IGP metric %d > %d",
+ "%s: %s loses to %s due to IGP metric %u > %u",
pfx_buf, new_buf, exist_buf, newm, existm);
ret = 0;
}
@@ -1025,7 +1025,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (newm < existm) {
if (debug)
zlog_debug(
- "%s: %s wins over %s due to CLUSTER_LIST length %d < %d",
+ "%s: %s wins over %s due to CLUSTER_LIST length %u < %u",
pfx_buf, new_buf, exist_buf,
newm, existm);
ret = 1;
@@ -1034,7 +1034,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
if (newm > existm) {
if (debug)
zlog_debug(
- "%s: %s loses to %s due to CLUSTER_LIST length %d > %d",
+ "%s: %s loses to %s due to CLUSTER_LIST length %u > %u",
pfx_buf, new_buf, exist_buf,
newm, existm);
ret = 0;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index d8bf9f0e9a..4b012430d9 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -740,7 +740,7 @@ int bgp_nb_errmsg_return(char *errmsg, size_t errmsg_len, int ret)
str = "Operation not allowed on a directly connected neighbor";
break;
case BGP_ERR_PEER_SAFI_CONFLICT:
- str = GR_INVALID;
+ str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'";
break;
case BGP_ERR_GR_INVALID_CMD:
str = "The Graceful Restart command used is not valid at this moment.";
@@ -832,7 +832,7 @@ int bgp_vty_return(struct vty *vty, int ret)
str = "Operation not allowed on a directly connected neighbor";
break;
case BGP_ERR_PEER_SAFI_CONFLICT:
- str = GR_INVALID;
+ str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'";
break;
case BGP_ERR_GR_INVALID_CMD:
str = "The Graceful Restart command used is not valid at this moment.";
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 9547de2869..c554332255 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -2864,7 +2864,7 @@ static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug(
- "%u:Recv MACIP %s f 0x%x MAC %pEA IP %pI4 VNI %u seq %u state %d ESI %s",
+ "%u:Recv MACIP %s f 0x%x MAC %pEA IP %pIA VNI %u seq %u state %d ESI %s",
vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
&mac, &ip, vni, seqnum, state,
esi_to_str(&esi, buf2, sizeof(buf2)));
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 33c8f3c1f0..d51ce817d1 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2203,7 +2203,7 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi)
/* If this is the first peer to be activated for this
* afi/labeled-unicast recalc bestpaths to trigger label allocation */
- if (safi == SAFI_LABELED_UNICAST
+ if (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST
&& !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
if (BGP_DEBUG(zebra, ZEBRA))
diff --git a/configure.ac b/configure.ac
index 139fca7c42..b034fab50c 100755
--- a/configure.ac
+++ b/configure.ac
@@ -1723,12 +1723,10 @@ fi
AS_IF([test "$enable_pathd" != "no"], [
AC_DEFINE([HAVE_PATHD], [1], [pathd])
-])
-
-AS_IF([test "$enable_pcep" != "no"], [
AC_DEFINE([HAVE_PATHD_PCEP], [1], [pathd-pcep])
])
+
if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then
AC_MSG_ERROR(["you must use json-c library to use bfdd"])
fi
@@ -2550,15 +2548,14 @@ AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"])
AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3])
dnl PCEP plugin
-AM_CONDITIONAL([HAVE_PATHD_PCEP], [test "$enable_pcep" = "yes"])
-AS_IF([test "$enable_pcep" = "yes"], [
- AC_CHECK_LIB([pcep_pcc], [initialize_pcc], [
- PATHD_PCEP_LIBS="-lpcep_pcc"
- ],[
- AC_MSG_ERROR([PCEP library libpcep_pcc not found])
- ])
- AC_SUBST([PATHD_PCEP_LIBS])
-])
+AS_IF([test "$enable_pathd" != "no"], [
+ AC_SUBST([PATHD_PCEP_LIBS], ["pceplib/libpcep_pcc.la"])
+ AC_SUBST([PATHD_PCEP_INCL], ["-I./pceplib "])
+ ])
+AC_CHECK_LIB([cunit], [CU_initialize_registry], [pcep_cunit=yes],[pcep_cunit=no])
+AM_CONDITIONAL([PATHD_PCEP_TEST], [test "x${pcep_cunit}" = xyes])
+AC_CHECK_PROG(VALGRIND_CHECK, valgrind, yes)
+AM_CONDITIONAL([HAVE_VALGRIND_PCEP], [test "$VALGRIND_CHECK" = "yes"])
dnl daemons
AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"])
@@ -2583,6 +2580,7 @@ AM_CONDITIONAL([STATICD], [test "$enable_staticd" != "no"])
AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"])
AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"])
AM_CONDITIONAL([PATHD], [test "$enable_pathd" != "no"])
+AM_CONDITIONAL([PATHD_PCEP], [test "$enable_pathd" != "no"])
AC_CONFIG_FILES([Makefile],[
test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build"
diff --git a/debian/frr.install b/debian/frr.install
index cefc3135b2..9972b579f0 100644
--- a/debian/frr.install
+++ b/debian/frr.install
@@ -11,6 +11,7 @@ usr/lib/*/frr/modules/dplane_fpm_nl.so
usr/lib/*/frr/modules/zebra_cumulus_mlag.so
usr/lib/*/frr/modules/zebra_fpm.so
usr/lib/*/frr/modules/zebra_irdp.so
+usr/lib/*/frr/modules/pathd_pcep.so
usr/lib/frr/*.sh
usr/lib/frr/*d
usr/lib/frr/watchfrr
diff --git a/doc/developer/cross-compiling.rst b/doc/developer/cross-compiling.rst
index 339e00c921..3bf78f7633 100644
--- a/doc/developer/cross-compiling.rst
+++ b/doc/developer/cross-compiling.rst
@@ -189,7 +189,7 @@ later on to FRR. One may get burned when compiling gRPC if the ``protoc``
version on the build machine differs from the version of ``protoc`` being linked
to during a gRPC build. The error messages from this defect look like:
-.. code-block:: terminal
+.. code-block:: shell
gens/src/proto/grpc/channelz/channelz.pb.h: In member function ‘void grpc::channelz::v1::ServerRef::set_name(const char*, size_t)’:
gens/src/proto/grpc/channelz/channelz.pb.h:9127:64: error: ‘EmptyDefault’ is not a member of ‘google::protobuf::internal::ArenaStringPtr’
diff --git a/doc/developer/images/PCEPlib_design.jpg b/doc/developer/images/PCEPlib_design.jpg
new file mode 100644
index 0000000000..41aada3774
--- /dev/null
+++ b/doc/developer/images/PCEPlib_design.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_internal_deps.jpg b/doc/developer/images/PCEPlib_internal_deps.jpg
new file mode 100644
index 0000000000..8380021b31
--- /dev/null
+++ b/doc/developer/images/PCEPlib_internal_deps.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_socket_comm.jpg b/doc/developer/images/PCEPlib_socket_comm.jpg
new file mode 100644
index 0000000000..3d62a462f2
--- /dev/null
+++ b/doc/developer/images/PCEPlib_socket_comm.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_threading_model.jpg b/doc/developer/images/PCEPlib_threading_model.jpg
new file mode 100644
index 0000000000..afe91c2407
--- /dev/null
+++ b/doc/developer/images/PCEPlib_threading_model.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg b/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
new file mode 100644
index 0000000000..5648a9d788
--- /dev/null
+++ b/doc/developer/images/PCEPlib_threading_model_frr_infra.jpg
Binary files differ
diff --git a/doc/developer/images/PCEPlib_timers.jpg b/doc/developer/images/PCEPlib_timers.jpg
new file mode 100644
index 0000000000..a178ee9c6f
--- /dev/null
+++ b/doc/developer/images/PCEPlib_timers.jpg
Binary files differ
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 8e7913419f..46fd8f612e 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -19,4 +19,5 @@ FRRouting Developer's Guide
zebra
vtysh
path
+ pceplib
link-state
diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst
index cf3aa8d17f..a35e60619c 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -71,6 +71,10 @@ Extensions
+-----------+--------------------------+----------------------------------------------+
| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` |
+-----------+--------------------------+----------------------------------------------+
+| ``%pIA`` | ``struct ipaddr *`` | ``1.2.3.4`` |
+| | | |
+| | | ``fe80::1234`` |
++-----------+--------------------------+----------------------------------------------+
| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` |
+-----------+--------------------------+----------------------------------------------+
| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` |
diff --git a/doc/developer/pceplib.rst b/doc/developer/pceplib.rst
new file mode 100644
index 0000000000..774617dfe5
--- /dev/null
+++ b/doc/developer/pceplib.rst
@@ -0,0 +1,781 @@
+.. _pceplib:
+
+*******
+PCEPlib
+*******
+
+Overview
+========
+
+The PCEPlib is a PCEP implementation library that can be used by either a PCE
+or PCC.
+
+Currently, only the FRR pathd has been implemented as a PCC with the PCEPlib.
+The PCEPlib is able to simultaneously connect to multiple PCEP peers and can
+maintain persistent PCEP connections.
+
+
+PCEPlib compliance
+==================
+
+The PCEPlib implements version 1 of the PCEP protocol, according to `RFC 5440 <https://tools.ietf.org/html/rfc5440>`_.
+
+Additionally, the PCEPlib implements the following PCEP extensions:
+
+- `RFC 8281 <https://tools.ietf.org/html/rfc8281>`_ PCE initiated for PCE-Initiated LSP Setup
+- `RFC 8231 <https://tools.ietf.org/html/rfc8231>`_ Extensions for Stateful PCE
+- `RFC 8232 <https://tools.ietf.org/html/rfc8232>`_ Optimizations of Label Switched Path State Synchronization Procedures for a Stateful PCE
+- `RFC 8282 <https://tools.ietf.org/html/rfc8282>`_ Extensions to PCEP for Inter-Layer MPLS and GMPLS Traffic Engineering
+- `RFC 8408 <https://tools.ietf.org/html/rfc8408>`_ Conveying Path Setup Type in PCE Communication Protocol (PCEP) Messages
+- `draft-ietf-pce-segment-routing-07 <https://tools.ietf.org/html/draft-ietf-pce-segment-routing-07>`_,
+ `draft-ietf-pce-segment-routing-16 <https://tools.ietf.org/html/draft-ietf-pce-segment-routing-16>`_,
+ `RFC 8664 <https://tools.ietf.org/html/rfc8664>`_ Segment routing protocol extensions
+- `RFC 7470 <https://tools.ietf.org/html/rfc7470>`_ Conveying Vendor-Specific Constraints
+- `Draft-ietf-pce-association-group-10 <https://tools.ietf.org/html/draft-ietf-pce-association-group-10>`_
+ Establishing Relationships Between Sets of Label Switched Paths
+- `Draft-barth-pce-segment-routing-policy-cp-04 <https://tools.ietf.org/html/draft-barth-pce-segment-routing-policy-cp-04>`_
+ Segment Routing Policy Candidate Paths
+
+
+PCEPlib Architecture
+====================
+
+The PCEPlib is comprised of the following modules, each of which will be
+detailed in the following sections.
+
+- **pcep_messages**
+ - PCEP messages, objects, and TLVs implementations
+
+- **pcep_pcc**
+ - PCEPlib public PCC API with a sample PCC binary
+
+- **pcep_session_logic**
+ - PCEP Session handling
+
+- **pcep_socket_comm**
+ - Socket communications
+
+- **pcep_timers**
+ - PCEP timers
+
+- **pcep_utils**
+ - Internal utilities used by the PCEPlib modules.
+
+The interaction of these modules can be seen in the following diagram.
+
+PCEPlib Architecture:
+
+.. image:: images/PCEPlib_design.jpg
+
+
+PCEP Session Logic library
+--------------------------
+
+The PCEP Session Logic library orchestrates calls to the rest of the PCC libraries.
+
+PCEP Session Logic library responsibilities:
+
+- Handle messages received from "PCEP Socket Comm"
+- Create and manage "PCEP Session" objects
+- Set timers and react to timer expirations
+- Manage counters
+
+The PCEP Session Logic library will have 2 main triggers controlled by a
+pthread condition variable:
+
+- Timer expirations - ``on_timer_expire()`` callback
+- Messages received from PCEP SocketComm - ``message_received()`` callback
+
+The counters are created and managed using the ``pcep_utils/pcep_utils_counters.h``
+counters library. The following are the different counter groups managed:
+
+- **COUNTER_SUBGROUP_ID_RX_MSG**
+- **COUNTER_SUBGROUP_ID_TX_MSG**
+- **COUNTER_SUBGROUP_ID_RX_OBJ**
+- **COUNTER_SUBGROUP_ID_TX_OBJ**
+- **COUNTER_SUBGROUP_ID_RX_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_TX_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ**
+- **COUNTER_SUBGROUP_ID_RX_TLV**
+- **COUNTER_SUBGROUP_ID_TX_TLV**
+- **COUNTER_SUBGROUP_ID_EVENT**
+
+The counters can be obtained and reset as explained later in the PCEPlib PCC API.
+
+PCEP Socket Comm library
+------------------------
+
+PCEP communication can be configured to be handled internally in this simple
+library. When this library is instantiated by the PCEP Session Logic, callbacks
+are provided to handle received messages and error conditions.
+
+The following diagram illustrates how the library works.
+
+PCEPlib Socket Comm:
+
+.. image:: images/PCEPlib_socket_comm.jpg
+
+
+PCEP Timers library
+-------------------
+
+Timers can be configured to be handled internally by this library. When this
+library is instantiated by the PCEP Session Logic, callbacks are provided to
+ha:0
+ndle timer expirations. The following timers are implemented and handled,
+according to `RFC 5440 <https://tools.ietf.org/html/rfc5440>`_.
+
+- Open KeepWait (fixed at 60 seconds)
+ - Set once the PCC sends an Open, and if it expires before receiving a KeepAlive or PCErr, then the PCC should send a PCErr and close the TCP connection
+
+- Keepalive timer
+ - How often the PCC should send Keepalive messages to the PCE (and vice-versa)
+ - The timer will be reset after any message is sent: any message serves as a Keepalive
+
+- DeadTimer
+ - If no messages are received before expiration, the session is declared as down
+ - Reset everytime any message is received
+
+- PCReq request timer
+ - How long the PCC waits for the PCE to reply to PCReq messages.
+
+PCEPlib Timers:
+
+.. image:: images/PCEPlib_timers.jpg
+
+
+PCEP Messages library
+---------------------
+
+The PCEP Messages library has all of the implemented PCEP messages, objects,
+TLVs, and related functionality.
+
+The following header files can be used for creating and handling received PCEP
+entities.
+
+- pcep-messages.h
+- pcep-objects.h
+- pcep-tlvs.h
+
+
+PCEP Messages
++++++++++++++
+
+The following PCEP messages can be created and received:
+
+- ``struct pcep_message* pcep_msg_create_open(...);``
+- ``struct pcep_message* pcep_msg_create_open_with_tlvs(...);``
+- ``struct pcep_message* pcep_msg_create_request(...);``
+- ``struct pcep_message* pcep_msg_create_request_ipv6(...);``
+- ``struct pcep_message* pcep_msg_create_reply(...);``
+- ``struct pcep_message* pcep_msg_create_close(...);``
+- ``struct pcep_message* pcep_msg_create_error(...);``
+- ``struct pcep_message* pcep_msg_create_error_with_objects(...);``
+- ``struct pcep_message* pcep_msg_create_keepalive(...);``
+- ``struct pcep_message* pcep_msg_create_report(...);``
+- ``struct pcep_message* pcep_msg_create_update(...);``
+- ``struct pcep_message* pcep_msg_create_initiate(...);``
+
+Refer to ``pcep_messages/include/pcep-messages.h`` and the API section
+below for more details.
+
+
+PCEP Objects
+++++++++++++
+
+The following PCEP objects can be created and received:
+
+- ``struct pcep_object_open* pcep_obj_create_open(...);``
+- ``struct pcep_object_rp* pcep_obj_create_rp(...);``
+- ``struct pcep_object_notify* pcep_obj_create_notify(...);``
+- ``struct pcep_object_nopath* pcep_obj_create_nopath(...);``
+- ``struct pcep_object_association_ipv4* pcep_obj_create_association_ipv4(...);``
+- ``struct pcep_object_association_ipv6* pcep_obj_create_association_ipv6(...);``
+- ``struct pcep_object_endpoints_ipv4* pcep_obj_create_endpoint_ipv4(...);``
+- ``struct pcep_object_endpoints_ipv6* pcep_obj_create_endpoint_ipv6(...);``
+- ``struct pcep_object_bandwidth* pcep_obj_create_bandwidth(...);``
+- ``struct pcep_object_metric* pcep_obj_create_metric(...);``
+- ``struct pcep_object_lspa* pcep_obj_create_lspa(...);``
+- ``struct pcep_object_svec* pcep_obj_create_svec(...);``
+- ``struct pcep_object_error* pcep_obj_create_error(...);``
+- ``struct pcep_object_close* pcep_obj_create_close(...);``
+- ``struct pcep_object_srp* pcep_obj_create_srp(...);``
+- ``struct pcep_object_lsp* pcep_obj_create_lsp(...);``
+- ``struct pcep_object_vendor_info* pcep_obj_create_vendor_info(...);``
+- ``struct pcep_object_ro* pcep_obj_create_ero(...);``
+- ``struct pcep_object_ro* pcep_obj_create_rro(...);``
+- ``struct pcep_object_ro* pcep_obj_create_iro(...);``
+- ``struct pcep_ro_subobj_ipv4* pcep_obj_create_ro_subobj_ipv4(...);``
+- ``struct pcep_ro_subobj_ipv6* pcep_obj_create_ro_subobj_ipv6(...);``
+- ``struct pcep_ro_subobj_unnum* pcep_obj_create_ro_subobj_unnum(...);``
+- ``struct pcep_ro_subobj_32label* pcep_obj_create_ro_subobj_32label(...);``
+- ``struct pcep_ro_subobj_asn* pcep_obj_create_ro_subobj_asn(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_nonai(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv4_node(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv6_node(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv4_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_ipv6_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(...);``
+- ``struct pcep_ro_subobj_sr* pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(...);``
+
+Refer to ``pcep_messages/include/pcep-objects.h`` and the API section
+below for more details.
+
+
+PCEP TLVs
++++++++++
+
+The following PCEP TLVs (Tag, Length, Value) can be created and received:
+
+- Open Object TLVs
+ - ``struct pcep_object_tlv_stateful_pce_capability* pcep_tlv_create_stateful_pce_capability(...);``
+ - ``struct pcep_object_tlv_lsp_db_version* pcep_tlv_create_lsp_db_version(...);``
+ - ``struct pcep_object_tlv_speaker_entity_identifier* pcep_tlv_create_speaker_entity_id(...);``
+ - ``struct pcep_object_tlv_path_setup_type* pcep_tlv_create_path_setup_type(...);``
+ - ``struct pcep_object_tlv_path_setup_type_capability* pcep_tlv_create_path_setup_type_capability(...);``
+ - ``struct pcep_object_tlv_sr_pce_capability* pcep_tlv_create_sr_pce_capability(...);``
+
+- LSP Object TLVs
+ - ``struct pcep_object_tlv_ipv4_lsp_identifier* pcep_tlv_create_ipv4_lsp_identifiers(...);``
+ - ``struct pcep_object_tlv_ipv6_lsp_identifier* pcep_tlv_create_ipv6_lsp_identifiers(...);``
+ - ``struct pcep_object_tlv_symbolic_path_name* pcep_tlv_create_symbolic_path_name(...);``
+ - ``struct pcep_object_tlv_lsp_error_code* pcep_tlv_create_lsp_error_code(...);``
+ - ``struct pcep_object_tlv_rsvp_error_spec* pcep_tlv_create_rsvp_ipv4_error_spec(...);``
+ - ``struct pcep_object_tlv_rsvp_error_spec* pcep_tlv_create_rsvp_ipv6_error_spec(...);``
+ - ``struct pcep_object_tlv_nopath_vector* pcep_tlv_create_nopath_vector(...);``
+ - ``struct pcep_object_tlv_vendor_info* pcep_tlv_create_vendor_info(...);``
+ - ``struct pcep_object_tlv_arbitrary* pcep_tlv_create_tlv_arbitrary(...);``
+
+- SRPAG (SR Association Group) TLVs
+ - ``struct pcep_object_tlv_srpag_pol_id *pcep_tlv_create_srpag_pol_id_ipv4(...);``
+ - ``struct pcep_object_tlv_srpag_pol_id *pcep_tlv_create_srpag_pol_id_ipv6(...);``
+ - ``struct pcep_object_tlv_srpag_pol_name *pcep_tlv_create_srpag_pol_name(...);``
+ - ``struct pcep_object_tlv_srpag_cp_id *pcep_tlv_create_srpag_cp_id(...);``
+ - ``struct pcep_object_tlv_srpag_cp_pref *pcep_tlv_create_srpag_cp_pref(...);``
+
+Refer to ``pcep_messages/include/pcep-tlvs.h`` and the API section
+below for more details.
+
+
+PCEP PCC
+--------
+
+This module has a Public PCC API library (explained in detail later) and a
+sample PCC binary. The APIs in this library encapsulate other PCEPlib libraries
+for simplicity. With this API, the PCEPlib PCC can be started and stopped, and
+the PCEPlib event queue can be accessed. The PCEP Messages library is not
+encapsulated, and should be used directly.
+
+
+Internal Dependencies
+---------------------
+
+The following diagram illustrates the internal PCEPlib library dependencies.
+
+PCEPlib internal dependencies:
+
+.. image:: images/PCEPlib_internal_deps.jpg
+
+
+External Dependencies
+---------------------
+
+Originally the PCEPlib was based on the open source `libpcep project <https://www.acreo.se/open-software-libpcep>`_,
+but that dependency has been reduced to just one source file (pcep-tools.[ch]).
+
+
+PCEPlib Threading model
+-----------------------
+
+The PCEPlib can be run in stand-alone mode whereby a thread is launched for
+timers and socket comm, as is illustrated in the following diagram.
+
+PCEPlib Threading model:
+
+.. image:: images/PCEPlib_threading_model.jpg
+
+The PCEPlib can also be configured to use an external timers and socket
+infrastructure like the FRR threads and tasks. In this case, no internal
+threads are launched for timers and socket comm, as is illustrated in the
+following diagram.
+
+PCEPlib Threading model with external infra:
+
+.. image:: images/PCEPlib_threading_model_frr_infra.jpg
+
+
+Building
+--------
+
+The autotools build system is used and integrated with the frr build system.
+
+Testing
+-------
+
+The Unit Tests for an individual library are executed with the ``make check``
+command. The Unit Test binary will be written to the project ``build`` directory.
+All Unit Tests are executed with Valgrind, and any memory issues reported by
+Valgrind will cause the Unit Test to fail.
+
+
+PCEPlib PCC API
+===============
+
+The following sections describe the PCEPlib PCC API.
+
+
+PCEPlib PCC Initialization and Destruction
+------------------------------------------
+
+The PCEPlib can be initialized to handle memory, timers, and socket comm
+internally in what is called stand-alone mode, or with an external
+infrastructure, like FRR.
+
+PCEPlib PCC Initialization and Destruction in stand-alone mode
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+PCEPlib PCC initialization and destruction functions:
+
+- ``bool initialize_pcc();``
+- ``bool initialize_pcc_wait_for_completion();``
+- ``bool destroy_pcc();``
+
+The PCC can be initialized with either ``initialize_pcc()`` or
+``initialize_pcc_wait_for_completion()``.
+
+- ``initialize_pcc_wait_for_completion()`` blocks until ``destroy_pcc()``
+ is called from a separate pthread.
+- ``initialize_pcc()`` is non-blocking and will be stopped when
+ ``destroy_pcc()`` is called.
+
+Both initialize functions will launch 3 pthreads:
+
+- 1 Timer pthread
+- 1 SocketComm pthread
+- 1 SessionLogic pthread
+
+When ``destroy_pcc()`` is called, all pthreads will be stopped and all
+resources will be released.
+
+All 3 functions return true upon success, and false otherwise.
+
+PCEPlib PCC Initialization and Destruction with FRR infrastructure
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+PCEPlib PCC initialization and destruction functions:
+
+- ``bool initialize_pcc_infra(struct pceplib_infra_config *infra_config);``
+- ``bool destroy_pcc();``
+
+The ``pceplib_infra_config`` struct has the following fields:
+
+- **void *pceplib_infra_mt**
+ - FRR Memory type pointer for infra related memory management
+
+- **void *pceplib_messages_mt**
+ - FRR Memory type pointer for PCEP messages related memory management
+
+- **pceplib_malloc_func mfunc**
+ - FRR malloc function pointer
+
+- **pceplib_calloc_func cfunc**
+ - FRR calloc function pointer
+
+- **pceplib_realloc_func rfunc**
+ - FRR realloc function pointer
+
+- **pceplib_strdup_func sfunc**
+ - FRR strdup function pointer
+
+- **pceplib_free_func ffunc**
+ - FRR free function pointer
+
+- **void *external_infra_data**
+ - FRR data used by FRR timers and sockets infrastructure
+
+- **ext_timer_create timer_create_func**
+ - FRR timer create function pointer
+
+- **ext_timer_cancel timer_cancel_func**
+ - FRR timer cancel function pointer
+
+- **ext_socket_write socket_write_func**
+ - FRR socket write function pointer, indicating fd is ready to be written to
+
+- **ext_socket_read socket_read_func**
+ - FRR socket write function pointer, indicating fd is ready to be read from
+
+
+PCEPlib PCC configuration
+-------------------------
+
+PCEPlib PCC configuratoin functions:
+
+- ``pcep_configuration *create_default_pcep_configuration();``
+- ``void destroy_pcep_configuration(pcep_configuration *config);``
+
+A ``pcep_configuration`` object with default values is created with
+``create_default_pcep_configuration()``. These values can be tailored to
+specific use cases.
+
+Created ``pcep_configuration`` objects are destroyed with
+``destroy_pcep_configuration()``.
+
+
+PCEPlib PCC configuration paramaters
+++++++++++++++++++++++++++++++++++++
+
+The ``pcep_configuration`` object is defined in ``pcep_session_logic/include/pcep_session_logic.h``
+The attributes in the ``pcep_configuration`` object are detailed as follows.
+
+PCEP Connection parameters:
+
+- **dst_pcep_port**
+ - Defaults to 0, in which case the default PCEP TCP destination port
+ 4189 will be used.
+ - Set to use a specific PCEP TCP destination port.
+
+- **src_pcep_port**
+ - Defaults to 0, in which case the default PCEP TCP source port
+ 4189 will be used.
+ - Set to use a specific PCEP TCP source port.
+
+- **Source IP**
+ - Defaults to IPv4 INADDR_ANY
+ - Set **src_ip.src_ipv4** and **is_src_ipv6=false** to set the source IPv4.
+ - Set **src_ip.src_ipv6** and **is_src_ipv6=true** to set the source IPv6.
+
+- **socket_connect_timeout_millis**
+ - Maximum amount of time to wait to connect to the PCE TCP socket
+ before failing, in milliseconds.
+
+PCEP Versioning:
+
+- **pcep_msg_versioning->draft_ietf_pce_segment_routing_07**
+ - Defaults to false, in which case draft 16 versioning will be used.
+ - Set to true to use draft 07 versioning.
+
+PCEP Open Message Parameters:
+
+- **keep_alive_seconds**
+ - Sent to PCE in PCEP Open Msg
+ - Recommended value = 30, Minimum value = 1
+ - Disabled by setting value = 0
+
+- **dead_timer_seconds**
+ - Sent to PCE in PCEP Open Msg
+ - Recommended value = 4 * keepalive timer value
+
+- Supported value ranges for PCEP Open Message received from the PCE
+ - **min_keep_alive_seconds**, **max_keep_alive_seconds**
+ - **min_dead_timer_seconds**, **max_dead_timer_seconds**
+
+- **request_time_seconds**
+ - When a PCC sends a PcReq to a PCE, the amount of time a PCC will
+ wait for a PcRep reply from the PCE.
+
+- **max_unknown_requests**
+ - If a PCC/PCE receives PCRep/PCReq messages with unknown requests
+ at a rate equal or greater than MAX-UNKNOWN-REQUESTS per minute,
+ the PCC/PCE MUST send a PCEP CLOSE message.
+ - Recommended value = 5
+
+- **max_unknown_messages**
+ - If a PCC/PCE receives unrecognized messages at a rate equal or
+ greater than MAX-UNKNOWN-MESSAGES per minute, the PCC/PCE MUST
+ send a PCEP CLOSE message
+ - Recommended value = 5
+
+Stateful PCE Capability TLV configuration parameters (RFC 8231, 8232, 8281, and
+draft-ietf-pce-segment-routing-16):
+
+- **support_stateful_pce_lsp_update**
+ - If this flag is true, then a Stateful PCE Capability TLV will
+ be added to the PCEP Open object, with the LSP Update Capability
+ U-flag set true.
+ - The rest of these parameters are used to configure the Stateful
+ PCE Capability TLV
+
+- **support_pce_lsp_instantiation**
+ - Sets the I-flag true, indicating the PCC allows instantiation
+ of an LSP by a PCE.
+
+- **support_include_db_version**
+ - Sets the S-bit true, indicating the PCC will include the
+ LSP-DB-VERSION TLV in each LSP object. See lsp_db_version below.
+
+- **support_lsp_triggered_resync**
+ - Sets the T-bit true, indicating the PCE can trigger resynchronization
+ of LSPs at any point in the life of the session.
+
+- **support_lsp_delta_sync**
+ - Sets the D-bit true, indicating the PCEP speaker allows incremental
+ (delta) State Synchronization.
+
+- **support_pce_triggered_initial_sync**
+ - Sets the F-bit true, indicating the PCE SHOULD trigger initial (first)
+ State Synchronization
+
+LSP DB Version TLV configuration parameters:
+
+- **lsp_db_version**
+ - If this parameter has a value other than 0, and the above
+ support_include_db_version flag is true, then an LSP DB
+ Version TLV will be added to the PCEP Open object.
+ - This parameter should only be set if LSP-DB survived a restart
+ and is available.
+ - This value will be copied over to the pcep_session upon initialization.
+
+SR PCE Capability sub-TLV configuration parameters (draft-ietf-pce-segment-routing-16):
+
+- **support_sr_te_pst**
+ - If this flag is true, then an SR PCE Capability sub-TLV will be
+ added to a Path Setup type Capability TLV, which will be added
+ to the PCEP Open object.
+ - The PST used in the Path Setup type Capability will be 1,
+ indicating the Path is setup using Segment Routing Traffic Engineering.
+
+Only set the following fields if the **support_sr_te_pst** flag is true.
+
+- **pcc_can_resolve_nai_to_sid**
+ - Sets the N-flag true, indicating that the PCC is capable of resolving
+ a Node or Adjacency Identifier to a SID
+
+- **max_sid_depth**
+ - If set other than 0, then the PCC imposes a limit on the Maximum
+ SID depth.
+ - If this parameter is other than 0, then the X bit will be true,
+ and the parameter value will be set in the MSD field.
+
+
+PCEPlib PCC connections
+-----------------------
+
+PCEPlib PCC connect and disconnect functions:
+
+- ``pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip);``
+- ``pcep_session *connect_pce_ipv6(pcep_configuration *config, struct in6_addr *pce_ip);``
+- ``void disconnect_pce(pcep_session *session);``
+
+When connecting to a PCE, a ``pcep_session`` will be returned on success, NULL
+otherwise.
+
+Refer to the above PCC configuration parameters section for setting the source
+and destination PCEP TCP ports, and the source IP address and version.
+
+
+PCEP Messages, Objects, and TLVs
+--------------------------------
+
+The PCEP messages, objects, and TLVs created in the PCEPlib are high-level API
+structures, meaning they need to be encoded before being sent on-the-wire, and
+the raw data received needs to be decoded into these structures. This makes
+using these objects much easier for the library consumer, since they do not
+need to know the detailed raw format of the PCEP entities.
+
+
+PCEP Messages
++++++++++++++
+
+Received messages (in the ``pcep_event`` explained below) are of type
+``pcep_message``, which have the following fields:
+
+- ``struct pcep_message_header *msg_header;``
+ - Defines the PCEP version and message type
+
+- ``double_linked_list *obj_list;``
+ - A double linked list of the message objects
+ - Each entry is a pointer to a ``struct pcep_object_header``, and
+ using the ``object_class`` and ``object_type`` fields, the pointer
+ can be cast to the appropriate object structure to access the
+ rest of the object fields
+
+- ``uint8_t *encoded_message;``
+ - This field is only populated for received messages or once the
+ ``pcep_encode_message()`` function has been called on the message.
+ - This field is a pointer to the raw PCEP data for the entire
+ message, including all objects and TLVs.
+
+- ``uint16_t encoded_message_length;``
+ - This field is only populated for received messages or once the
+ ``pcep_encode_message()`` function has been called on the message.
+ - This field is the length of the entire raw message, including
+ all objects and TLVs.
+ - This field is in host byte order.
+
+
+PCEP Objects
+++++++++++++
+
+A PCEP message has a double linked list of pointers to ``struct pcep_object_header``
+structures, which have the following fields:
+
+- ``enum pcep_object_classes object_class;``
+- ``enum pcep_object_types object_type;``
+- ``bool flag_p;``
+ - PCC Processing rule bit: When set, the object MUST be taken into
+ account, when cleared the object is optional
+
+- ``bool flag_i;``
+ - PCE Ignore bit: indicates to a PCC whether or not an optional
+ object was processed
+
+- ``double_linked_list *tlv_list;``
+ - A double linked list of the object TLVs
+ - Each entry is a pointer to a ``struct pcep_object_tlv_header``, and
+ using the TLV type field, the pointer can be cast to the
+ appropriate TLV structure to access the rest of the TLV fields
+
+- ``uint8_t *encoded_object;``
+ - This field is only populated for received objects or once the
+ ``pcep_encode_object()`` (called by ``pcep_encode_message()``)
+ function has been called on the object.
+ - Pointer into the encoded_message field (from the pcep_message)
+ where the raw object PCEP data starts.
+
+- ``uint16_t encoded_object_length;``
+ - This field is only populated for received objects or once the
+ ``pcep_encode_object()`` (called by ``pcep_encode_message()``)
+ function has been called on the object.
+ - This field is the length of the entire raw TLV
+ - This field is in host byte order.
+
+The object class and type can be used to cast the ``struct pcep_object_header``
+pointer to the appropriate object structure so the specific object fields can
+be accessed.
+
+
+PCEP TLVs
++++++++++
+
+A PCEP object has a double linked list of pointers to ``struct pcep_object_tlv_header``
+structures, which have the following fields:
+
+- ``enum pcep_object_tlv_types type;``
+- ``uint8_t *encoded_tlv;``
+ - This field is only populated for received TLVs or once the
+ ``pcep_encode_tlv()`` (called by ``pcep_encode_message()``)
+ function has been called on the TLV.
+ - Pointer into the encoded_message field (from the pcep_message)
+ where the raw TLV PCEP data starts.
+
+- ``uint16_t encoded_tlv_length;``
+ - This field is only populated for received TLVs or once the
+ ``pcep_encode_tlv()`` (called by ``pcep_encode_message()``)
+ function has been called on the TLV.
+ - This field is the length of the entire raw TLV
+ - This field is in host byte order.
+
+
+Memory management
++++++++++++++++++
+
+Any of the PCEPlib Message Library functions that receive a pointer to a
+``double_linked_list``, ``pcep_object_header``, or ``pcep_object_tlv_header``,
+transfer the ownership of the entity to the PCEPlib. The memory will be freed
+internally when the encapsulating structure is freed. If the memory for any of
+these is freed by the caller, then there will be a double memory free error
+when the memory is freed internally in the PCEPlib.
+
+Any of the PCEPlib Message Library functions that receive either a pointer to a
+``struct in_addr`` or ``struct in6_addr`` will allocate memory for the IP
+address internally and copy the IP address. It is the responsibility of the
+caller to manage the memory for the IP address passed into the PCEPlib Message
+Library functions.
+
+For messages received via the event queue (explained below), the message will
+be freed when the event is freed by calling ``destroy_pcep_event()``.
+
+When sending messages, the message will be freed internally in the PCEPlib when
+the ``send_message()`` ``pcep_pcc`` API function when the ``free_after_send`` flag
+is set true.
+
+To manually delete a message, call the ``pcep_msg_free_message()`` function.
+Internally, this will call ``pcep_obj_free_object()`` and ``pcep_obj_free_tlv()``
+appropriately.
+
+
+Sending a PCEP Report message
+-----------------------------
+
+This section shows how to send a PCEP Report messages from the PCC to the PCE,
+and serves as an example of how to send other messages. Refer to the sample
+PCC binary located in ``pcep_pcc/src/pcep_pcc.c`` for code examples os sending
+a PCEP Report message.
+
+The Report message must have at least an SRP, LSP, and ERO object.
+
+The PCEP Report message objects are created with the following APIs:
+
+- ``struct pcep_object_srp *pcep_obj_create_srp(...);``
+- ``struct pcep_object_lsp *pcep_obj_create_lsp(...);``
+- ``struct pcep_object_ro *pcep_obj_create_ero(...);``
+ - Create ero subobjects with the ``pcep_obj_create_ro_subobj_*(...);`` functions
+
+PCEP Report message is created with the following API:
+
+- ``struct pcep_header *pcep_msg_create_report(double_linked_list *report_object_list);``
+
+A PCEP report messages is sent with the following API:
+
+- ``void send_message(pcep_session *session, pcep_message *message, bool free_after_send);``
+
+
+PCEPlib Received event queue
+----------------------------
+
+PCEP events and messages of interest to the PCEPlib consumer will be stored
+internally in a message queue for retrieval.
+
+The following are the event types:
+
+- **MESSAGE_RECEIVED**
+- **PCE_CLOSED_SOCKET**
+- **PCE_SENT_PCEP_CLOSE**
+- **PCE_DEAD_TIMER_EXPIRED**
+- **PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED**
+- **PCC_CONNECTED_TO_PCE**
+- **PCC_CONNECTION_FAILURE**
+- **PCC_PCEP_SESSION_CLOSED**
+- **PCC_RCVD_INVALID_OPEN**
+- **PCC_SENT_INVALID_OPEN**
+- **PCC_RCVD_MAX_INVALID_MSGS**
+- **PCC_RCVD_MAX_UNKOWN_MSGS**
+
+The following PCEP messages will not be posted on the message queue, as they
+are handled internally in the library:
+
+- **Open**
+- **Keep Alive**
+- **Close**
+
+Received event queue API:
+
+- ``bool event_queue_is_empty();``
+ - Returns true if the queue is empty, false otherwise
+
+- ``uint32_t event_queue_num_events_available();``
+ - Return the number of events on the queue, 0 if empty
+
+- ``struct pcep_event *event_queue_get_event();``
+ - Return the next event on the queue, NULL if empty
+ - The ``message`` pointer will only be non-NULL if ``event_type``
+ is ``MESSAGE_RECEIVED``
+
+- ``void destroy_pcep_event(struct pcep_event *event);``
+ - Free the PCEP Event resources, including the PCEP message if present
+
+
+PCEPlib Counters
+----------------
+
+The PCEPlib counters are managed in the ``pcep_session_logic`` library, and can
+be accessed in the ``pcep_session_counters`` field of the ``pcep_session`` structure.
+There are 2 API functions to manage the counters:
+
+- ``void dump_pcep_session_counters(pcep_session *session);``
+ - Dump all of the counters to the logs
+
+- ``void reset_pcep_session_counters(pcep_session *session);``
+
diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst
index b0413619ab..708f65ff7d 100644
--- a/doc/developer/scripting.rst
+++ b/doc/developer/scripting.rst
@@ -271,7 +271,7 @@ has). For example, here is the encoder function for ``struct prefix``:
This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is:
-.. code-block::
+.. code-block:: c
{ ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index f7e4486ef0..d16420c7e7 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -57,6 +57,7 @@ dev_RSTFILES = \
doc/developer/tracing.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
+ doc/developer/topotests-markers.rst \
doc/developer/topotests.rst \
doc/developer/workflow.rst \
doc/developer/xrefs.rst \
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 93d81548b2..7976a206f7 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -388,11 +388,19 @@ This is the recommended test writing routine:
- Format the new code using `black <https://github.com/psf/black>`_
- Create a Pull Request
-.. Note::
+Some things to keep in mind:
+
+- BGP tests MUST use generous convergence timeouts - you must ensure
+ that any test involving BGP uses a convergence timeout of at least
+ 130 seconds.
+- Topotests are run on a range of Linux versions: if your test
+ requires some OS-specific capability (like mpls support, or vrf
+ support), there are test functions available in the libraries that
+ will help you determine whether your test should run or be skipped.
+- Avoid including unstable data in your test: don't rely on link-local
+ addresses or ifindex values, for example, because these can change
+ from run to run.
- BGP tests MUST use generous convergence timeouts - you must ensure
- that any test involving BGP uses a convergence timeout of at least
- 130 seconds.
Topotest File Hierarchy
"""""""""""""""""""""""
@@ -795,7 +803,7 @@ Requirements:
- Use `black <https://github.com/psf/black>`_ code formatter before creating
a pull request. This ensures we have a unified code style.
- Mark test modules with pytest markers depending on the daemons used during the
- tests (s. Markers)
+ tests (see :ref:`topotests-markers`)
Tips:
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index b8f749b740..7d136b183e 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -166,15 +166,22 @@ BFD peers and profiles share the same BFD session configuration commands.
The minimum transmission interval (less jitter) that this system
wants to use to send BFD control packets. Defaults to 300ms.
-.. clicmd:: echo-interval (10-60000)
+.. clicmd:: echo receive-interval <disabled|(10-60000)>
- Configures the minimal echo receive transmission interval that this
- system is capable of handling.
+ Configures the minimum interval that this system is capable of
+ receiving echo packets. Disabled means that this system doesn't want
+ to receive echo packets. The default value is 50 milliseconds.
+
+.. clicmd:: echo transmit-interval (10-60000)
+
+ The minimum transmission interval (less jitter) that this system
+ wants to use to send BFD echo packets. Defaults to 50ms.
.. clicmd:: echo-mode
Enables or disables the echo transmission mode. This mode is disabled
- by default.
+ by default. If you are not using distributed BFD then echo mode works
+ only when the peer is also FRR.
It is recommended that the transmission interval of control packets
to be increased after enabling echo-mode to reduce bandwidth usage.
@@ -445,12 +452,13 @@ You can inspect the current BFD peer status with the following commands:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
+ Echo receive interval: 50ms
Echo transmission interval: disabled
Remote timers:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
- Echo transmission interval: 50ms
+ Echo receive interval: 50ms
peer 192.168.1.1
label: router3-peer
@@ -465,12 +473,13 @@ You can inspect the current BFD peer status with the following commands:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
+ Echo receive interval: 50ms
Echo transmission interval: disabled
Remote timers:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
- Echo transmission interval: 50ms
+ Echo receive interval: 50ms
frr# show bfd peer 192.168.1.1
BFD Peer:
@@ -487,15 +496,16 @@ You can inspect the current BFD peer status with the following commands:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
+ Echo receive interval: 50ms
Echo transmission interval: disabled
Remote timers:
Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
- Echo transmission interval: 50ms
+ Echo receive interval: 50ms
frr# show bfd peer 192.168.0.1 json
- {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"}
+ {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-receive-interval":50,"echo-transmit-interval":0,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-receive-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"}
You can inspect the current BFD peer status in brief with the following commands:
diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c
index 00d8ea8867..ae15e97d4a 100644
--- a/eigrpd/eigrp_cli.c
+++ b/eigrpd/eigrp_cli.c
@@ -671,7 +671,7 @@ DEFPY_YANG(
as_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
- snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-address", xpath);
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath);
nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, prefix_str);
return nb_cli_apply_changes(vty, NULL);
@@ -694,7 +694,7 @@ DEFPY_YANG(
as_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
- snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-address", xpath);
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath);
nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, prefix_str);
return nb_cli_apply_changes(vty, NULL);
@@ -703,12 +703,12 @@ DEFPY_YANG(
void eigrp_cli_show_summarize_address(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
- const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
- true);
+ const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance");
+ uint16_t asn = yang_dnode_get_uint16(instance, "./asn");
const char *summarize_address = yang_dnode_get_string(dnode, NULL);
- vty_out(vty, " ip summary-address eigrp %d %s\n",
- eif->eigrp->AS, summarize_address);
+ vty_out(vty, " ip summary-address eigrp %d %s\n", asn,
+ summarize_address);
}
/*
@@ -767,12 +767,11 @@ DEFPY_YANG(
void eigrp_cli_show_authentication(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
- const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
- true);
+ const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance");
+ uint16_t asn = yang_dnode_get_uint16(instance, "./asn");
const char *crypt = yang_dnode_get_string(dnode, NULL);
- vty_out(vty, " ip authentication mode eigrp %d %s\n",
- eif->eigrp->AS, crypt);
+ vty_out(vty, " ip authentication mode eigrp %d %s\n", asn, crypt);
}
/*
@@ -827,12 +826,12 @@ DEFPY_YANG(
void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
- const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
- true);
+ const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance");
+ uint16_t asn = yang_dnode_get_uint16(instance, "./asn");
const char *keychain = yang_dnode_get_string(dnode, NULL);
- vty_out(vty, " ip authentication key-chain eigrp %d %s\n",
- eif->eigrp->AS, keychain);
+ vty_out(vty, " ip authentication key-chain eigrp %d %s\n", asn,
+ keychain);
}
diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c
index 252cd647a2..7eee254627 100644
--- a/eigrpd/eigrp_packet.c
+++ b/eigrpd/eigrp_packet.c
@@ -572,17 +572,11 @@ int eigrp_read(struct thread *thread)
/* If incoming interface is passive one, ignore it. */
if (eigrp_if_is_passive(ei)) {
- char buf[3][INET_ADDRSTRLEN];
-
if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV))
zlog_debug(
- "ignoring packet from router %s sent to %s, received on a passive interface, %s",
- inet_ntop(AF_INET, &eigrph->vrid, buf[0],
- sizeof(buf[0])),
- inet_ntop(AF_INET, &iph->ip_dst, buf[1],
- sizeof(buf[1])),
- inet_ntop(AF_INET, &ei->address.u.prefix4,
- buf[2], sizeof(buf[2])));
+ "ignoring packet from router %u sent to %pI4, received on a passive interface, %pI4",
+ ntohs(eigrph->vrid), &iph->ip_dst,
+ &ei->address.u.prefix4);
if (iph->ip_dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS)) {
eigrp_if_set_multicast(ei);
diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c
index e79123e6b4..7c765248c6 100644
--- a/eigrpd/eigrp_zebra.c
+++ b/eigrpd/eigrp_zebra.c
@@ -231,9 +231,7 @@ void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p,
api.nexthop_num = count;
if (IS_DEBUG_EIGRP(zebra, ZEBRA_REDISTRIBUTE)) {
- char buf[PREFIX_STRLEN];
- zlog_debug("Zebra: Route add %pFX nexthop %s", p,
- inet_ntop(AF_INET, 0, buf, PREFIX_STRLEN));
+ zlog_debug("Zebra: Route add %pFX", p);
}
zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index 71d4758163..3c3a68764e 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -49,13 +49,21 @@
#include "isisd/fabricd.h"
#include "isisd/isis_nb.h"
-static struct isis_adjacency *adj_alloc(const uint8_t *id)
+static struct isis_adjacency *adj_alloc(struct isis_circuit *circuit,
+ const uint8_t *id)
{
struct isis_adjacency *adj;
adj = XCALLOC(MTYPE_ISIS_ADJACENCY, sizeof(struct isis_adjacency));
memcpy(adj->sysid, id, ISIS_SYS_ID_LEN);
+ adj->snmp_idx = ++circuit->snmp_adj_idx_gen;
+
+ if (circuit->snmp_adj_list == NULL)
+ circuit->snmp_adj_list = list_new();
+
+ adj->snmp_list_node = listnode_add(circuit->snmp_adj_list, adj);
+
return adj;
}
@@ -65,7 +73,7 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
struct isis_adjacency *adj;
int i;
- adj = adj_alloc(id); /* P2P kludge */
+ adj = adj_alloc(circuit, id); /* P2P kludge */
if (snpa) {
memcpy(adj->snpa, snpa, ETH_ALEN);
@@ -146,6 +154,8 @@ void isis_delete_adj(void *arg)
if (!adj)
return;
+ /* Remove self from snmp list without walking the list*/
+ list_delete_node(adj->circuit->snmp_adj_list, adj->snmp_list_node);
thread_cancel(&adj->t_expire);
if (adj->adj_state != ISIS_ADJ_DOWN)
@@ -292,7 +302,6 @@ void isis_adj_state_change(struct isis_adjacency **padj,
if (circuit->area->log_adj_changes)
isis_log_adj_change(adj, old_state, new_state, reason);
- circuit->adj_state_changes++;
#ifndef FABRICD
/* send northbound notification */
isis_notif_adj_state_change(adj, new_state, reason);
@@ -303,12 +312,14 @@ void isis_adj_state_change(struct isis_adjacency **padj,
if ((adj->level & level) == 0)
continue;
if (new_state == ISIS_ADJ_UP) {
+ circuit->adj_state_changes++;
circuit->upadjcount[level - 1]++;
/* update counter & timers for debugging
* purposes */
adj->last_flap = time(NULL);
adj->flaps++;
} else if (old_state == ISIS_ADJ_UP) {
+ circuit->adj_state_changes++;
listnode_delete(circuit->u.bc.adjdb[level - 1],
adj);
diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h
index 2780d826f5..3afb7209f3 100644
--- a/isisd/isis_adjacency.h
+++ b/isisd/isis_adjacency.h
@@ -105,6 +105,8 @@ struct isis_adjacency {
unsigned int mt_count; /* Number of entries in mt_set */
struct bfd_session *bfd_session;
struct list *adj_sids; /* Segment Routing Adj-SIDs. */
+ uint32_t snmp_idx;
+ struct listnode *snmp_list_node;
};
struct isis_threeway_adj;
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 4aac3f8880..62822cbf89 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -71,6 +71,48 @@ DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp))
int isis_if_new_hook(struct interface *);
int isis_if_delete_hook(struct interface *);
+static int isis_circuit_smmp_id_gen(struct isis_circuit *circuit)
+{
+ struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct isis *isis = NULL;
+ uint32_t id;
+ uint32_t i;
+
+ isis = isis_lookup_by_vrfid(vrf->vrf_id);
+ if (isis == NULL)
+ return 0;
+
+ id = isis->snmp_circuit_id_last;
+ id++;
+
+ /* find next unused entry */
+ for (i = 0; i < SNMP_CIRCUITS_MAX; i++) {
+ if (id >= SNMP_CIRCUITS_MAX) {
+ id = 0;
+ continue;
+ }
+
+ if (id == 0)
+ continue;
+
+ if (isis->snmp_circuits[id] == NULL)
+ break;
+
+ id++;
+ }
+
+ if (i == SNMP_CIRCUITS_MAX) {
+ zlog_warn("Could not allocate a smmp-circuit-id");
+ return 0;
+ }
+
+ isis->snmp_circuits[id] = circuit;
+ isis->snmp_circuit_id_last = id;
+ circuit->snmp_id = id;
+
+ return 1;
+}
+
struct isis_circuit *isis_circuit_new(struct isis *isis)
{
struct isis_circuit *circuit;
@@ -80,6 +122,12 @@ struct isis_circuit *isis_circuit_new(struct isis *isis)
circuit->isis = isis;
/*
+ * Note: if snmp-id generation failed circuit will fail
+ * up operation
+ */
+ isis_circuit_smmp_id_gen(circuit);
+
+ /*
* Default values
*/
#ifndef FABRICD
@@ -150,11 +198,18 @@ struct isis_circuit *isis_circuit_new(struct isis *isis)
void isis_circuit_del(struct isis_circuit *circuit)
{
+ struct isis *isis = NULL;
+
if (!circuit)
return;
QOBJ_UNREG(circuit);
+ if (circuit->interface) {
+ isis = isis_lookup_by_vrfid(circuit->interface->vrf_id);
+ isis->snmp_circuits[circuit->snmp_id] = NULL;
+ }
+
isis_circuit_if_unbind(circuit, circuit->interface);
circuit_mt_finish(circuit);
@@ -609,6 +664,7 @@ int isis_circuit_up(struct isis_circuit *circuit)
return ISIS_OK;
if (circuit->is_passive) {
+ circuit->last_uptime = time(NULL);
/* make sure the union fields are initialized, else we
* could end with garbage values from a previous circuit
* type, which would then cause a segfault when building
@@ -623,6 +679,13 @@ int isis_circuit_up(struct isis_circuit *circuit)
return ISIS_OK;
}
+ if (circuit->snmp_id == 0) {
+ /* We cannot bring circuit up if does not have snmp-id */
+ flog_err(EC_ISIS_CONFIG,
+ "No snnmp-id: there are too many circuits:");
+ return ISIS_ERROR;
+ }
+
if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) {
flog_err(
EC_ISIS_CONFIG,
@@ -722,6 +785,8 @@ int isis_circuit_up(struct isis_circuit *circuit)
circuit->tx_queue = isis_tx_queue_new(circuit, send_lsp);
+ circuit->last_uptime = time(NULL);
+
#ifndef FABRICD
/* send northbound notification */
isis_notif_if_state_change(circuit, false);
@@ -828,6 +893,15 @@ void isis_circuit_down(struct isis_circuit *circuit)
thread_cancel(&circuit->u.p2p.t_send_p2p_hello);
}
+ /*
+ * All adjacencies have to be gone, delete snmp list
+ * and reset snmpd idx generator
+ */
+ if (circuit->snmp_adj_list != NULL)
+ list_delete(&circuit->snmp_adj_list);
+
+ circuit->snmp_adj_idx_gen = 0;
+
/* Cancel all active threads */
thread_cancel(&circuit->t_send_csnp[0]);
thread_cancel(&circuit->t_send_csnp[1]);
diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h
index 3387232da2..15d58bd736 100644
--- a/isisd/isis_circuit.h
+++ b/isisd/isis_circuit.h
@@ -79,6 +79,7 @@ struct isis_circuit_arg {
struct isis_circuit {
int state;
uint8_t circuit_id; /* l1/l2 bcast CircuitID */
+ time_t last_uptime;
struct isis *isis;
struct isis_area *area; /* back pointer to the area */
struct interface *interface; /* interface info from z */
@@ -115,6 +116,8 @@ struct isis_circuit {
int pad_hellos; /* add padding to Hello PDUs ? */
char ext_domain; /* externalDomain (boolean) */
int lsp_regenerate_pending[ISIS_LEVELS];
+ uint64_t lsp_error_counter;
+
/*
* Configurables
*/
@@ -165,6 +168,12 @@ struct isis_circuit {
uint32_t auth_type_failures; /*authentication-type-fails */
uint32_t auth_failures; /* authentication-fails */
+ uint32_t snmp_id; /* Circuit id in snmp */
+
+ uint32_t snmp_adj_idx_gen; /* Create unique id for adjacency on creation
+ */
+ struct list *snmp_adj_list; /* List in id order */
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(isis_circuit)
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c
index f6175fe9a4..e09e23aaeb 100644
--- a/isisd/isis_dr.c
+++ b/isisd/isis_dr.c
@@ -97,6 +97,7 @@ static int isis_check_dr_change(struct isis_adjacency *adj, int level)
/* was there a DIS state transition ? */
{
adj->dischanges[level - 1]++;
+ adj->circuit->desig_changes[level - 1]++;
/* ok rotate the history list through */
for (i = DIS_RECORDS - 1; i > 0; i--) {
adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c
index 244f388c26..d2c5d93e25 100644
--- a/isisd/isis_dynhn.c
+++ b/isisd/isis_dynhn.c
@@ -166,3 +166,38 @@ void dynhn_print_all(struct vty *vty, struct isis *isis)
cmd_hostname_get());
return;
}
+
+struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level)
+{
+ struct listnode *node = NULL;
+ struct isis_dynhn *dyn = NULL;
+ struct isis_dynhn *found_dyn = NULL;
+ int res;
+
+ for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) {
+ res = memcmp(dyn->id, id, ISIS_SYS_ID_LEN);
+
+ if (res < 0)
+ continue;
+
+ if (res == 0 && dyn->level <= level)
+ continue;
+
+ if (res == 0) {
+ /*
+ * This is the best match, we can stop
+ * searching
+ */
+
+ found_dyn = dyn;
+ break;
+ }
+
+ if (found_dyn == NULL
+ || memcmp(dyn->id, found_dyn->id, ISIS_SYS_ID_LEN) < 0) {
+ found_dyn = dyn;
+ }
+ }
+
+ return found_dyn;
+}
diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h
index 973fde8307..8d25582e49 100644
--- a/isisd/isis_dynhn.h
+++ b/isisd/isis_dynhn.h
@@ -38,4 +38,7 @@ struct isis_dynhn *dynhn_find_by_id(const uint8_t *id);
struct isis_dynhn *dynhn_find_by_name(const char *hostname);
void dynhn_print_all(struct vty *vty, struct isis *isis);
+/* Snmp support */
+struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level);
+
#endif /* _ZEBRA_ISIS_DYNHN_H */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index a17d9a6ae2..06a5a69e3f 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -324,8 +324,8 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno)
/* check for overflow */
if (newseq < lsp->hdr.seqno) {
/* send northbound notification */
- isis_notif_lsp_exceed_max(lsp->area,
- rawlspid_print(lsp->hdr.lsp_id));
+ lsp->area->lsp_exceeded_max_counter++;
+ isis_notif_lsp_exceed_max(lsp->area, lsp->hdr.lsp_id);
}
#endif /* ifndef FABRICD */
@@ -1357,8 +1357,8 @@ int lsp_generate(struct isis_area *area, int level)
#ifndef FABRICD
/* send northbound notification */
- isis_notif_lsp_gen(area, rawlspid_print(newlsp->hdr.lsp_id),
- newlsp->hdr.seqno, newlsp->last_generated);
+ isis_notif_lsp_gen(area, newlsp->hdr.lsp_id, newlsp->hdr.seqno,
+ newlsp->last_generated);
#endif /* ifndef FABRICD */
return ISIS_OK;
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index dfa77fbaca..a6841b9fd4 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -549,40 +549,97 @@ void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty,
/* Notifications. */
void isis_notif_db_overload(const struct isis_area *area, bool overload);
void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
- uint32_t pdu_size, const char *lsp_id);
+ uint32_t pdu_size, const uint8_t *lsp_id);
void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down);
void isis_notif_corrupted_lsp(const struct isis_area *area,
- const char *lsp_id); /* currently unused */
+ const uint8_t *lsp_id); /* currently unused */
void isis_notif_lsp_exceed_max(const struct isis_area *area,
- const char *lsp_id);
+ const uint8_t *lsp_id);
void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
uint8_t max_area_addrs,
- const char *raw_pdu);
+ const char *raw_pdu, size_t raw_pdu_len);
void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
- const char *raw_pdu);
+ const char *raw_pdu,
+ size_t raw_pdu_len);
void isis_notif_authentication_failure(const struct isis_circuit *circuit,
- const char *raw_pdu);
+ const char *raw_pdu, size_t raw_pdu_len);
void isis_notif_adj_state_change(const struct isis_adjacency *adj,
int new_state, const char *reason);
void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
- const char *reason, const char *raw_pdu);
+ const char *reason, const char *raw_pdu,
+ size_t raw_pdu_len);
void isis_notif_area_mismatch(const struct isis_circuit *circuit,
- const char *raw_pdu);
+ const char *raw_pdu, size_t raw_pdu_len);
void isis_notif_lsp_received(const struct isis_circuit *circuit,
- const char *lsp_id, uint32_t seqno,
+ const uint8_t *lsp_id, uint32_t seqno,
uint32_t timestamp, const char *sys_id);
-void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
+void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id,
uint32_t seqno, uint32_t timestamp);
void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
- uint8_t rcv_id_len, const char *raw_pdu);
+ uint8_t rcv_id_len, const char *raw_pdu,
+ size_t raw_pdu_len);
void isis_notif_version_skew(const struct isis_circuit *circuit,
- uint8_t version, const char *raw_pdu);
+ uint8_t version, const char *raw_pdu,
+ size_t raw_pdu_len);
void isis_notif_lsp_error(const struct isis_circuit *circuit,
- const char *lsp_id, const char *raw_pdu,
- uint32_t offset, uint8_t tlv_type);
+ const uint8_t *lsp_id, const char *raw_pdu,
+ size_t raw_pdu_len, uint32_t offset,
+ uint8_t tlv_type);
void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
- const char *lsp_id);
+ const uint8_t *lsp_id);
void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
- const char *lsp_id);
+ const uint8_t *lsp_id);
+
+/* We also declare hook for every notification */
+
+DECLARE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area));
+DECLARE_HOOK(isis_hook_lsp_too_large,
+ (const struct isis_circuit *circuit, uint32_t pdu_size,
+ const uint8_t *lsp_id),
+ (circuit, pdu_size, lsp_id));
+/* Note: no isis_hook_corrupted_lsp - because this notificaiton is not used */
+DECLARE_HOOK(isis_hook_lsp_exceed_max,
+ (const struct isis_area *area, const uint8_t *lsp_id),
+ (area, lsp_id));
+DECLARE_HOOK(isis_hook_max_area_addr_mismatch,
+ (const struct isis_circuit *circuit, uint8_t max_addrs,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, max_addrs, raw_pdu, raw_pdu_len));
+DECLARE_HOOK(isis_hook_authentication_type_failure,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DECLARE_HOOK(isis_hook_authentication_failure,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DECLARE_HOOK(isis_hook_adj_state_change, (const struct isis_adjacency *adj),
+ (adj));
+DECLARE_HOOK(isis_hook_reject_adjacency,
+ (const struct isis_circuit *circuit, const char *pdu,
+ size_t pdu_len),
+ (circuit, pdu, pdu_len));
+DECLARE_HOOK(isis_hook_area_mismatch,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit));
+DECLARE_HOOK(isis_hook_id_len_mismatch,
+ (const struct isis_circuit *circuit, uint8_t rcv_id_len,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, rcv_id_len, raw_pdu, raw_pdu_len));
+DECLARE_HOOK(isis_hook_version_skew,
+ (const struct isis_circuit *circuit, uint8_t version,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit));
+DECLARE_HOOK(isis_hook_lsp_error,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit));
+DECLARE_HOOK(isis_hook_seqno_skipped,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id),
+ (circuit, lsp_id));
+DECLARE_HOOK(isis_hook_own_lsp_purge,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id),
+ (circuit, lsp_id));
#endif /* ISISD_ISIS_NB_H_ */
diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c
index ea33ec10ec..755378a9b7 100644
--- a/isisd/isis_nb_notifications.c
+++ b/isisd/isis_nb_notifications.c
@@ -28,6 +28,56 @@
#include "isisd/isis_dynhn.h"
#include "isisd/isis_misc.h"
+DEFINE_HOOK(isis_hook_lsp_too_large,
+ (const struct isis_circuit *circuit, uint32_t pdu_size,
+ const uint8_t *lsp_id),
+ (circuit, pdu_size, lsp_id));
+DEFINE_HOOK(isis_hook_corrupted_lsp, (const struct isis_area *area), (area));
+DEFINE_HOOK(isis_hook_lsp_exceed_max,
+ (const struct isis_area *area, const uint8_t *lsp_id),
+ (area, lsp_id));
+DEFINE_HOOK(isis_hook_max_area_addr_mismatch,
+ (const struct isis_circuit *circuit, uint8_t max_addrs,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, max_addrs, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_authentication_type_failure,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_authentication_failure,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_adj_state_change, (const struct isis_adjacency *adj),
+ (adj));
+DEFINE_HOOK(isis_hook_reject_adjacency,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_area_mismatch,
+ (const struct isis_circuit *circuit, const char *raw_pdu,
+ size_t raw_pdu_len),
+ (circuit, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_id_len_mismatch,
+ (const struct isis_circuit *circuit, uint8_t rcv_id_len,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, rcv_id_len, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_version_skew,
+ (const struct isis_circuit *circuit, uint8_t version,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, version, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_lsp_error,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id,
+ const char *raw_pdu, size_t raw_pdu_len),
+ (circuit, lsp_id, raw_pdu, raw_pdu_len));
+DEFINE_HOOK(isis_hook_seqno_skipped,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id),
+ (circuit, lsp_id));
+DEFINE_HOOK(isis_hook_own_lsp_purge,
+ (const struct isis_circuit *circuit, const uint8_t *lsp_id),
+ (circuit, lsp_id));
+
+
/*
* Helper functions.
*/
@@ -92,7 +142,7 @@ void isis_notif_db_overload(const struct isis_area *area, bool overload)
* XPath: /frr-isisd:lsp-too-large
*/
void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
- uint32_t pdu_size, const char *lsp_id)
+ uint32_t pdu_size, const uint8_t *lsp_id)
{
const char *xpath = "/frr-isisd:lsp-too-large";
struct list *arguments = yang_data_list_new();
@@ -106,9 +156,11 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
data = yang_data_new_uint32(xpath_arg, pdu_size);
listnode_add(arguments, data);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
+ hook_call(isis_hook_lsp_too_large, circuit, pdu_size, lsp_id);
+
nb_notification_send(xpath, arguments);
}
@@ -135,7 +187,8 @@ void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down)
/*
* XPath: /frr-isisd:corrupted-lsp-detected
*/
-void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id)
+void isis_notif_corrupted_lsp(const struct isis_area *area,
+ const uint8_t *lsp_id)
{
const char *xpath = "/frr-isisd:corrupted-lsp-detected";
struct list *arguments = yang_data_list_new();
@@ -144,16 +197,19 @@ void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id)
notif_prep_instance_hdr(xpath, area, "default", arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
+ hook_call(isis_hook_corrupted_lsp, area);
+
nb_notification_send(xpath, arguments);
}
/*
* XPath: /frr-isisd:attempt-to-exceed-max-sequence
*/
-void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id)
+void isis_notif_lsp_exceed_max(const struct isis_area *area,
+ const uint8_t *lsp_id)
{
const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence";
struct list *arguments = yang_data_list_new();
@@ -162,9 +218,11 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id)
notif_prep_instance_hdr(xpath, area, "default", arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
+ hook_call(isis_hook_lsp_exceed_max, area, lsp_id);
+
nb_notification_send(xpath, arguments);
}
@@ -173,7 +231,7 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id)
*/
void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
uint8_t max_area_addrs,
- const char *raw_pdu)
+ const char *raw_pdu, size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:max-area-addresses-mismatch";
struct list *arguments = yang_data_list_new();
@@ -190,6 +248,9 @@ void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_max_area_addr_mismatch, circuit, max_area_addrs,
+ raw_pdu, raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -197,7 +258,8 @@ void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit,
* XPath: /frr-isisd:authentication-type-failure
*/
void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
- const char *raw_pdu)
+ const char *raw_pdu,
+ size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:authentication-type-failure";
struct list *arguments = yang_data_list_new();
@@ -211,6 +273,9 @@ void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_authentication_type_failure, circuit, raw_pdu,
+ raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -218,7 +283,7 @@ void isis_notif_authentication_type_failure(const struct isis_circuit *circuit,
* XPath: /frr-isisd:authentication-failure
*/
void isis_notif_authentication_failure(const struct isis_circuit *circuit,
- const char *raw_pdu)
+ const char *raw_pdu, size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:authentication-failure";
struct list *arguments = yang_data_list_new();
@@ -232,6 +297,9 @@ void isis_notif_authentication_failure(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_authentication_failure, circuit, raw_pdu,
+ raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -269,6 +337,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj,
listnode_add(arguments, data);
}
+ hook_call(isis_hook_adj_state_change, adj);
+
nb_notification_send(xpath, arguments);
}
@@ -276,7 +346,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj,
* XPath: /frr-isisd:rejected-adjacency
*/
void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
- const char *reason, const char *raw_pdu)
+ const char *reason, const char *raw_pdu,
+ size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:rejected-adjacency";
struct list *arguments = yang_data_list_new();
@@ -293,6 +364,8 @@ void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_reject_adjacency, circuit, raw_pdu, raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -300,7 +373,7 @@ void isis_notif_reject_adjacency(const struct isis_circuit *circuit,
* XPath: /frr-isisd:area-mismatch
*/
void isis_notif_area_mismatch(const struct isis_circuit *circuit,
- const char *raw_pdu)
+ const char *raw_pdu, size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:area-mismatch";
struct list *arguments = yang_data_list_new();
@@ -314,6 +387,8 @@ void isis_notif_area_mismatch(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_area_mismatch, circuit, raw_pdu, raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -321,7 +396,7 @@ void isis_notif_area_mismatch(const struct isis_circuit *circuit,
* XPath: /frr-isisd:lsp-received
*/
void isis_notif_lsp_received(const struct isis_circuit *circuit,
- const char *lsp_id, uint32_t seqno,
+ const uint8_t *lsp_id, uint32_t seqno,
uint32_t timestamp, const char *sys_id)
{
const char *xpath = "/frr-isisd:lsp-received";
@@ -333,7 +408,7 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit,
notif_prep_instance_hdr(xpath, area, "default", arguments);
notif_prepr_iface_hdr(xpath, circuit, arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
data = yang_data_new_uint32(xpath_arg, seqno);
@@ -351,7 +426,7 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit,
/*
* XPath: /frr-isisd:lsp-generation
*/
-void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
+void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id,
uint32_t seqno, uint32_t timestamp)
{
const char *xpath = "/frr-isisd:lsp-generation";
@@ -361,7 +436,7 @@ void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
notif_prep_instance_hdr(xpath, area, "default", arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
data = yang_data_new_uint32(xpath_arg, seqno);
@@ -377,7 +452,8 @@ void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id,
* XPath: /frr-isisd:id-len-mismatch
*/
void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
- uint8_t rcv_id_len, const char *raw_pdu)
+ uint8_t rcv_id_len, const char *raw_pdu,
+ size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:id-len-mismatch";
struct list *arguments = yang_data_list_new();
@@ -394,6 +470,9 @@ void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_id_len_mismatch, circuit, rcv_id_len, raw_pdu,
+ raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -401,7 +480,8 @@ void isis_notif_id_len_mismatch(const struct isis_circuit *circuit,
* XPath: /frr-isisd:version-skew
*/
void isis_notif_version_skew(const struct isis_circuit *circuit,
- uint8_t version, const char *raw_pdu)
+ uint8_t version, const char *raw_pdu,
+ size_t raw_pdu_len)
{
const char *xpath = "/frr-isisd:version-skew";
struct list *arguments = yang_data_list_new();
@@ -418,6 +498,9 @@ void isis_notif_version_skew(const struct isis_circuit *circuit,
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
+ hook_call(isis_hook_version_skew, circuit, version, raw_pdu,
+ raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -425,7 +508,8 @@ void isis_notif_version_skew(const struct isis_circuit *circuit,
* XPath: /frr-isisd:lsp-error-detected
*/
void isis_notif_lsp_error(const struct isis_circuit *circuit,
- const char *lsp_id, const char *raw_pdu,
+ const uint8_t *lsp_id, const char *raw_pdu,
+ size_t raw_pdu_len,
__attribute__((unused)) uint32_t offset,
__attribute__((unused)) uint8_t tlv_type)
{
@@ -438,13 +522,15 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit,
notif_prep_instance_hdr(xpath, area, "default", arguments);
notif_prepr_iface_hdr(xpath, circuit, arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
data = yang_data_new(xpath_arg, raw_pdu);
listnode_add(arguments, data);
/* ignore offset and tlv_type which cannot be set properly */
+ hook_call(isis_hook_lsp_error, circuit, lsp_id, raw_pdu, raw_pdu_len);
+
nb_notification_send(xpath, arguments);
}
@@ -452,7 +538,7 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit,
* XPath: /frr-isisd:sequence-number-skipped
*/
void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
- const char *lsp_id)
+ const uint8_t *lsp_id)
{
const char *xpath = "/frr-isisd:sequence-number-skipped";
struct list *arguments = yang_data_list_new();
@@ -463,9 +549,11 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
notif_prep_instance_hdr(xpath, area, "default", arguments);
notif_prepr_iface_hdr(xpath, circuit, arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
+ hook_call(isis_hook_seqno_skipped, circuit, lsp_id);
+
nb_notification_send(xpath, arguments);
}
@@ -473,7 +561,7 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
* XPath: /frr-isisd:own-lsp-purge
*/
void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
- const char *lsp_id)
+ const uint8_t *lsp_id)
{
const char *xpath = "/frr-isisd:own-lsp-purge";
struct list *arguments = yang_data_list_new();
@@ -484,8 +572,10 @@ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
notif_prep_instance_hdr(xpath, area, "default", arguments);
notif_prepr_iface_hdr(xpath, circuit, arguments);
snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
- data = yang_data_new_string(xpath_arg, lsp_id);
+ data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
listnode_add(arguments, data);
+ hook_call(isis_hook_own_lsp_purge, circuit, lsp_id);
+
nb_notification_send(xpath, arguments);
}
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index a02b48157f..7256fcbbc7 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -549,6 +549,19 @@ static int pdu_len_validate(uint16_t pdu_len, struct isis_circuit *circuit)
return 0;
}
+static void update_rej_adj_count(struct isis_circuit *circuit)
+{
+ circuit->rej_adjacencies++;
+ if (circuit->is_type == IS_LEVEL_1)
+ circuit->area->rej_adjacencies[0]++;
+ else if (circuit->is_type == IS_LEVEL_2)
+ circuit->area->rej_adjacencies[1]++;
+ else {
+ circuit->area->rej_adjacencies[0]++;
+ circuit->area->rej_adjacencies[1]++;
+ }
+}
+
static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
uint8_t *ssnpa)
{
@@ -581,22 +594,22 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
if (p2p_hello) {
if (circuit->circ_type != CIRCUIT_T_P2P) {
zlog_warn("p2p hello on non p2p circuit");
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(
circuit, "p2p hello on non p2p circuit",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
} else {
if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
zlog_warn("lan hello on non broadcast circuit");
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(
circuit, "lan hello on non broadcast circuit",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -605,12 +618,12 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
zlog_debug(
"level %d LAN Hello received over circuit with externalDomain = true",
level);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(
circuit,
"LAN Hello received over circuit with externalDomain = true",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -622,10 +635,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
circuit->area->area_tag,
circuit->interface->name);
}
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
- isis_notif_reject_adjacency(
- circuit, "Interface level mismatch", raw_pdu);
+ isis_notif_reject_adjacency(circuit,
+ "Interface level mismatch",
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -652,10 +666,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
"ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %hu",
circuit->area->area_tag, pdu_name,
circuit->interface->name, iih.pdu_len);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(circuit, "Invalid PDU length",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -664,10 +678,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
flog_err(EC_ISIS_PACKET,
"Level %d LAN Hello with Circuit Type %d", level,
iih.circ_type);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
- isis_notif_reject_adjacency(
- circuit, "LAN Hello with wrong IS-level", raw_pdu);
+ isis_notif_reject_adjacency(circuit,
+ "LAN Hello with wrong IS-level",
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_ERROR;
}
@@ -678,10 +693,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream),
circuit->rcv_stream, &iih.tlvs, &error_log)) {
zlog_warn("isis_unpack_tlvs() failed: %s", error_log);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(circuit, "Failed to unpack TLVs",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
@@ -690,17 +705,18 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
zlog_warn("No Area addresses TLV in %s", pdu_name);
#ifndef FABRICD
/* send northbound notification */
- isis_notif_area_mismatch(circuit, raw_pdu);
+ isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
if (!iih.tlvs->protocols_supported.count) {
zlog_warn("No supported protocols TLV in %s", pdu_name);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
- isis_notif_reject_adjacency(
- circuit, "No supported protocols TLV", raw_pdu);
+ isis_notif_reject_adjacency(circuit,
+ "No supported protocols TLV",
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
@@ -716,12 +732,13 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
pdu_end - pdu_start);
if (auth_code == ISIS_AUTH_FAILURE) {
- circuit->auth_failures++;
- isis_notif_authentication_failure(circuit, raw_pdu);
+ update_rej_adj_count(circuit);
+ isis_notif_authentication_failure(circuit, raw_pdu,
+ sizeof(raw_pdu));
} else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
- circuit->auth_type_failures++;
- isis_notif_authentication_type_failure(circuit,
- raw_pdu);
+ update_rej_adj_count(circuit);
+ isis_notif_authentication_type_failure(circuit, raw_pdu,
+ sizeof(raw_pdu));
}
#endif /* ifndef FABRICD */
goto out;
@@ -731,10 +748,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
zlog_warn(
"ISIS-Adj (%s): Received IIH with own sysid on %s - discard",
circuit->area->area_tag, circuit->interface->name);
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
- isis_notif_reject_adjacency(
- circuit, "Received IIH with our own sysid", raw_pdu);
+ isis_notif_reject_adjacency(circuit,
+ "Received IIH with our own sysid",
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
@@ -752,7 +770,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
}
#ifndef FABRICD
/* send northbound notification */
- isis_notif_area_mismatch(circuit, raw_pdu);
+ isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
@@ -769,11 +787,11 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
"ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH",
circuit->area->area_tag);
}
- circuit->rej_adjacencies++;
+ update_rej_adj_count(circuit);
#ifndef FABRICD
isis_notif_reject_adjacency(
circuit, "Neither IPv4 not IPv6 considered usable",
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
goto out;
}
@@ -857,8 +875,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
#ifndef FABRICD
/* send northbound notification */
- isis_notif_lsp_received(circuit, rawlspid_print(hdr.lsp_id), hdr.seqno,
- time(NULL), sysid_print(hdr.lsp_id));
+ isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL),
+ sysid_print(hdr.lsp_id));
#endif /* ifndef FABRICD */
if (pdu_len_validate(hdr.pdu_len, circuit)) {
@@ -931,8 +949,18 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
* we change the code above to return those extra fields, we
* will send dummy values which are ignored in the callback
*/
- isis_notif_lsp_error(circuit, rawlspid_print(hdr.lsp_id),
- raw_pdu, 0, 0);
+ circuit->lsp_error_counter++;
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->lsp_error_counter[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->lsp_error_counter[1]++;
+ } else {
+ circuit->area->lsp_error_counter[0]++;
+ circuit->area->lsp_error_counter[1]++;
+ }
+
+ isis_notif_lsp_error(circuit, hdr.lsp_id, raw_pdu,
+ sizeof(raw_pdu), 0, 0);
#endif /* ifndef FABRICD */
goto out;
}
@@ -956,11 +984,28 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
/* send northbound notification */
if (auth_code == ISIS_AUTH_FAILURE) {
circuit->auth_failures++;
- isis_notif_authentication_failure(circuit, raw_pdu);
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->auth_failures[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->auth_failures[1]++;
+ } else {
+ circuit->area->auth_failures[0]++;
+ circuit->area->auth_failures[1]++;
+ }
+ isis_notif_authentication_failure(circuit, raw_pdu,
+ sizeof(raw_pdu));
} else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
circuit->auth_type_failures++;
- isis_notif_authentication_type_failure(circuit,
- raw_pdu);
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->auth_type_failures[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->auth_type_failures[1]++;
+ } else {
+ circuit->area->auth_type_failures[0]++;
+ circuit->area->auth_type_failures[1]++;
+ }
+ isis_notif_authentication_type_failure(circuit, raw_pdu,
+ sizeof(raw_pdu));
}
#endif /* ifndef FABRICD */
goto out;
@@ -1105,10 +1150,10 @@ dontcheckadj:
if (lsp->hdr.seqno < hdr.seqno) {
/* send northbound
* notification */
+ circuit->area
+ ->lsp_seqno_skipped_counter++;
isis_notif_seqno_skipped(
- circuit,
- rawlspid_print(
- hdr.lsp_id));
+ circuit, hdr.lsp_id);
}
#endif /* ifndef FABRICD */
lsp_inc_seqno(lsp, hdr.seqno);
@@ -1129,8 +1174,7 @@ dontcheckadj:
/* our own LSP with 0 remaining life time */
#ifndef FABRICD
/* send northbound notification */
- isis_notif_own_lsp_purge(
- circuit, rawlspid_print(hdr.lsp_id));
+ isis_notif_own_lsp_purge(circuit, hdr.lsp_id);
#endif /* ifndef FABRICD */
}
}
@@ -1158,8 +1202,8 @@ dontcheckadj:
lsp_inc_seqno(lsp, hdr.seqno);
#ifndef FABRICD
/* send northbound notification */
- isis_notif_seqno_skipped(circuit,
- rawlspid_print(hdr.lsp_id));
+ circuit->area->lsp_seqno_skipped_counter++;
+ isis_notif_seqno_skipped(circuit, hdr.lsp_id);
#endif /* ifndef FABRICD */
if (IS_DEBUG_UPDATE_PACKETS) {
zlog_debug(
@@ -1388,12 +1432,28 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
pdu_end - pdu_start);
if (auth_code == ISIS_AUTH_FAILURE) {
circuit->auth_failures++;
- isis_notif_authentication_failure(circuit,
- raw_pdu);
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->auth_failures[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->auth_failures[1]++;
+ } else {
+ circuit->area->auth_failures[0]++;
+ circuit->area->auth_failures[1]++;
+ }
+ isis_notif_authentication_failure(
+ circuit, raw_pdu, sizeof(raw_pdu));
} else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
circuit->auth_type_failures++;
- isis_notif_authentication_type_failure(circuit,
- raw_pdu);
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->auth_type_failures[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->auth_type_failures[1]++;
+ } else {
+ circuit->area->auth_type_failures[0]++;
+ circuit->area->auth_type_failures[1]++;
+ }
+ isis_notif_authentication_type_failure(
+ circuit, raw_pdu, sizeof(raw_pdu));
}
#endif /* ifndef FABRICD */
goto out;
@@ -1620,7 +1680,8 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
zlog_warn("Unsupported ISIS version %hhu", version1);
#ifndef FABRICD
/* send northbound notification */
- isis_notif_version_skew(circuit, version1, raw_pdu);
+ isis_notif_version_skew(circuit, version1, raw_pdu,
+ sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -1631,9 +1692,19 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
"IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u",
id_len, ISIS_SYS_ID_LEN);
circuit->id_len_mismatches++;
+ if (circuit->is_type == IS_LEVEL_1) {
+ circuit->area->id_len_mismatches[0]++;
+ } else if (circuit->is_type == IS_LEVEL_2) {
+ circuit->area->id_len_mismatches[1]++;
+ } else {
+ circuit->area->id_len_mismatches[0]++;
+ circuit->area->id_len_mismatches[1]++;
+ }
+
#ifndef FABRICD
/* send northbound notification */
- isis_notif_id_len_mismatch(circuit, id_len, raw_pdu);
+ isis_notif_id_len_mismatch(circuit, id_len, raw_pdu,
+ sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_ERROR;
}
@@ -1662,7 +1733,8 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
zlog_warn("Unsupported ISIS PDU version %hhu", version2);
#ifndef FABRICD
/* send northbound notification */
- isis_notif_version_skew(circuit, version2, raw_pdu);
+ isis_notif_version_skew(circuit, version2, raw_pdu,
+ sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_WARNING;
}
@@ -1686,7 +1758,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
#ifndef FABRICD
/* send northbound notification */
isis_notif_max_area_addr_mismatch(circuit, max_area_addrs,
- raw_pdu);
+ raw_pdu, sizeof(raw_pdu));
#endif /* ifndef FABRICD */
return ISIS_ERROR;
}
@@ -2409,7 +2481,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
#ifndef FABRICD
/* send a northbound notification */
isis_notif_lsp_too_large(circuit, stream_get_endp(lsp->pdu),
- rawlspid_print(lsp->hdr.lsp_id));
+ lsp->hdr.lsp_id);
#endif /* ifndef FABRICD */
if (IS_DEBUG_PACKET_DUMP)
zlog_dump_data(STREAM_DATA(lsp->pdu),
diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c
new file mode 100644
index 0000000000..50dc0f2a1c
--- /dev/null
+++ b/isisd/isis_snmp.c
@@ -0,0 +1,3457 @@
+/*
+ * ISIS SNMP support
+ * Copyright (C) 2020 Volta Networks, Inc.
+ * Aleksey Romanov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This is minimal read-only implementations providing isisReadOnlyCompliance
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "vrf.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "memory.h"
+#include "smux.h"
+#include "libfrr.h"
+#include "version.h"
+
+#include "isisd/isis_constants.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_flags.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_network.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_constants.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_te.h"
+#include "isisd/isis_dr.h"
+#include "isisd/isis_nb.h"
+#include "isisd/isisd.h"
+
+/* ISIS-MIB. */
+#define ISIS_MIB 1, 3, 6, 1, 2, 1, 138
+
+#define ISIS_OBJECTS 1
+#define ISIS_SYSTEM 1, 1
+#define ISIS_SYSLEVEL 1, 2
+#define ISIS_CIRC 1, 3
+#define ISIS_CIRC_LEVEL_VALUES 1, 4
+#define ISIS_COUNTERS 1, 5
+#define ISIS_ISADJ 1, 6
+
+/************************ isisSystemGroup ************************/
+
+/* isisSysObject */
+#define ISIS_SYS_OBJECT 1, 1, 1
+#define ISIS_SYS_VERSION 1
+#define ISIS_SYS_LEVELTYPE 2
+#define ISIS_SYS_ID 3
+#define ISIS_SYS_MAXPATHSPLITS 4
+#define ISIS_SYS_MAXLSPGENINT 5
+#define ISIS_SYS_POLLESHELLORATE 6
+#define ISIS_SYS_WAITTIME 7
+#define ISIS_SYS_ADMINSTATE 8
+#define ISIS_SYS_L2TOL1LEAKING 9
+#define ISIS_SYS_MAXAGE 10
+#define ISIS_SYS_RECEIVELSPBUFFERSIZE 11
+#define ISIS_SYS_PROTSUPPORTED 12
+#define ISIS_SYS_NOTIFICATIONENABLE 13
+
+/* isisManAreaAddrEntry */
+#define ISIS_MANAREA_ADDRENTRY 1, 1, 2, 1
+#define ISIS_MANAREA_ADDREXISTSTATE 2
+
+/* isisAreaAddrEntry */
+#define ISIS_AREA_ADDRENTRY 1, 1, 3, 1
+#define ISIS_AREA_ADDR 1
+
+/* isisSummAddrEntry */
+#define ISIS_SUMM_ADDRENTRY 1, 1, 4, 1
+#define ISIS_SUMM_ADDREXISTSTATE 4
+#define ISIS_SUMM_ADDRMETRIC 5
+#define ISIS_SUMM_ADDRFULLMETRIC 6
+
+/* isisRedistributeAddrEntry */
+#define ISIS_REDISTRIBUTE_ADDRENTRY 1, 1, 5, 1
+#define ISIS_REDISTRIBUTE_ADDREXISTSTATE 3
+
+/* isisRouterEntry */
+#define ISIS_ROUTER_ENTRY 1, 1, 6, 1
+#define ISIS_ROUTER_HOSTNAME 3
+#define ISIS_ROUTER_ID 4
+
+/* isisSysLevelTable */
+#define ISIS_SYSLEVEL_ENTRY 1, 2, 1, 1
+#define ISIS_SYSLEVEL_ORIGLSPBUFFSIZE 2
+#define ISIS_SYSLEVEL_MINLSPGENINT 3
+#define ISIS_SYSLEVEL_STATE 4
+#define ISIS_SYSLEVEL_SETOVERLOAD 5
+#define ISIS_SYSLEVEL_SETOVERLOADUNTIL 6
+#define ISIS_SYSLEVEL_METRICSTYLE 7
+#define ISIS_SYSLEVEL_SPFCONSIDERS 8
+#define ISIS_SYSLEVEL_TEENABLED 9
+
+
+/* isisSystemCounterEntry */
+#define ISIS_SYSTEM_COUNTER_ENTRY 1, 5, 1, 1
+#define ISIS_SYSSTAT_CORRLSPS 2
+#define ISIS_SYSSTAT_AUTHTYPEFAILS 3
+#define ISIS_SYSSTAT_AUTHFAILS 4
+#define ISIS_SYSSTAT_LSPDBASEOLOADS 5
+#define ISIS_SYSSTAT_MANADDRDROPFROMAREAS 6
+#define ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS 7
+#define ISIS_SYSSTAT_SEQNUMSKIPS 8
+#define ISIS_SYSSTAT_OWNLSPPURGES 9
+#define ISIS_SYSSTAT_IDFIELDLENMISMATCHES 10
+#define ISIS_SYSSTAT_PARTCHANGES 11
+#define ISIS_SYSSTAT_SPFRUNS 12
+#define ISIS_SYSSTAT_LSPERRORS 13
+
+
+/************************ isisCircuitGroup ************************/
+
+/* Scalar directly under isisCirc */
+#define ISIS_NEXTCIRC_INDEX 1
+
+/* isisCircEntry */
+#define ISIS_CIRC_ENTRY 1, 3, 2, 1
+#define ISIS_CIRC_IFINDEX 2
+#define ISIS_CIRC_ADMINSTATE 3
+#define ISIS_CIRC_EXISTSTATE 4
+#define ISIS_CIRC_TYPE 5
+#define ISIS_CIRC_EXTDOMAIN 6
+#define ISIS_CIRC_LEVELTYPE 7
+#define ISIS_CIRC_PASSIVECIRCUIT 8
+#define ISIS_CIRC_MESHGROUPENABLED 9
+#define ISIS_CIRC_MESHGROUP 10
+#define ISIS_CIRC_SMALLHELLOS 11
+#define ISIS_CIRC_LASTUPTIME 12
+#define ISIS_CIRC_3WAYENABLED 13
+#define ISIS_CIRC_EXTENDEDCIRCID 14
+
+/* isisCircLevelEntry */
+#define ISIS_CIRCLEVEL_ENTRY 1, 4, 1, 1
+#define ISIS_CIRCLEVEL_METRIC 2
+#define ISIS_CIRCLEVEL_WIDEMETRIC 3
+#define ISIS_CIRCLEVEL_ISPRIORITY 4
+#define ISIS_CIRCLEVEL_IDOCTET 5
+#define ISIS_CIRCLEVEL_ID 6
+#define ISIS_CIRCLEVEL_DESIS 7
+#define ISIS_CIRCLEVEL_HELLOMULTIPLIER 8
+#define ISIS_CIRCLEVEL_HELLOTIMER 9
+#define ISIS_CIRCLEVEL_DRHELLOTIMER 10
+#define ISIS_CIRCLEVEL_LSPTHROTTLE 11
+#define ISIS_CIRCLEVEL_MINLSPRETRANSINT 12
+#define ISIS_CIRCLEVEL_CSNPINTERVAL 13
+#define ISIS_CIRCLEVEL_PARTSNPINTERVAL 14
+
+/* isisCircuitCounterEntry */
+#define ISIS_CIRC_COUNTER_ENTRY 1, 5, 2, 1
+#define ISIS_CIRC_ADJCHANGES 2
+#define ISIS_CIRC_NUMADJ 3
+#define ISIS_CIRC_INITFAILS 4
+#define ISIS_CIRC_REJADJS 5
+#define ISIS_CIRC_IDFIELDLENMISMATCHES 6
+#define ISIS_CIRC_MAXAREAADDRMISMATCHES 7
+#define ISIS_CIRC_AUTHTYPEFAILS 8
+#define ISIS_CIRC_AUTHFAILS 9
+#define ISIS_CIRC_LANDESISCHANGES 10
+
+
+/************************ isisISAdjGroup ************************/
+
+/* isisISAdjEntry */
+#define ISIS_ISADJ_ENTRY 1, 6, 1, 1
+#define ISIS_ISADJ_STATE 2
+#define ISIS_ISADJ_3WAYSTATE 3
+#define ISIS_ISADJ_NEIGHSNPAADDRESS 4
+#define ISIS_ISADJ_NEIGHSYSTYPE 5
+#define ISIS_ISADJ_NEIGHSYSID 6
+#define ISIS_ISADJ_NBREXTENDEDCIRCID 7
+#define ISIS_ISADJ_USAGE 8
+#define ISIS_ISADJ_HOLDTIMER 9
+#define ISIS_ISADJ_NEIGHPRIORITY 10
+#define ISIS_ISADJ_LASTUPTIME 11
+
+/* isisISAdjAreadAddrEntry */
+#define ISIS_ISADJAREA_ADDRENTRY 1, 6, 2, 1
+#define ISIS_ISADJAREA_ADDRESS 2
+
+/* isisISAdjIPAddrEntry*/
+#define ISIS_ISADJIPADDR_ENTRY 1, 6, 3, 1
+#define ISIS_ISADJIPADDR_TYPE 2
+#define ISIS_ISADJIPADDR_ADDRESS 3
+
+
+/* isisISAdjProtSuppEntty */
+
+#define ISIS_ISADJPROTSUPP_ENTRY 1, 6, 4, 1
+#define ISIS_ISADJPROTSUPP_PROTOCOL 1
+
+
+/************************ Trap data variables ************************/
+#define ISIS_NOTIFICATION_ENTRY 1, 10, 1
+#define ISIS_NOTIF_SYLELVELINDEX 1
+#define ISIS_NOTIF_CIRCIFINDEX 2
+#define ISIS_PDU_LSPID 3
+#define ISIS_PDU_FRAGMENT 4
+#define ISIS_PDU_FIELDLEN 5
+#define ISIS_PDU_MAXAREAADDR 6
+#define ISIS_PDU_PROTOVER 7
+#define ISIS_PDU_LSPSIZE 8
+#define ISIS_PDU_ORIGBUFFERSIZE 9
+#define ISIS_PDU_BUFFERSIZE 10
+#define ISIS_PDU_PROTSUPP 11
+#define ISIS_ADJ_STATE 12
+#define ISIS_ERROR_OFFSET 13
+#define ISIS_ERROR_TLVTYPE 14
+#define ISIS_NOTIF_AREAADDR 15
+
+/************************ Traps ************************/
+#define ISIS_NOTIFICATIONS ISIS_MIB, 0
+#define ISIS_TRAP_DB_OVERLOAD 1
+#define ISIS_TRAP_MAN_ADDR_DROP 2
+#define ISIS_TRAP_CORRUPTED_LSP 3
+#define ISIS_TRAP_LSP_EXCEED_MAX 4
+#define ISIS_TRAP_ID_LEN_MISMATCH 5
+#define ISIS_TRAP_MAX_AREA_ADDR_MISMATCH 6
+#define ISIS_TRAP_OWN_LSP_PURGE 7
+#define ISIS_TRAP_SEQNO_SKIPPED 8
+#define ISIS_TRAP_AUTHEN_TYPE_FAILURE 9
+#define ISIS_TRAP_AUTHEN_FAILURE 10
+#define ISIS_TRAP_VERSION_SKEW 11
+#define ISIS_TRAP_AREA_MISMATCH 12
+#define ISIS_TRAP_REJ_ADJACENCY 13
+#define ISIS_TRAP_LSP_TOO_LARGE 14
+#define ISIS_TRAP_LSP_BUFFSIZE_MISMATCH 15
+#define ISIS_TRAP_PROTSUPP_MISMATCH 16
+#define ISIS_TRAP_ADJ_STATE_CHANGE 17
+#define ISIS_TRAP_LSP_ERROR 18
+
+/* Change this definition if number of traps changes */
+#define ISIS_TRAP_LAST_TRAP ISIS_TRAP_LSP_ERROR + 1
+
+#define ISIS_SNMP_TRAP_VAR 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0
+
+
+/* SNMP value hack. */
+#define COUNTER32 ASN_COUNTER
+#define INTEGER ASN_INTEGER
+#define UNSIGNED32 ASN_GAUGE
+#define TIMESTAMP ASN_TIMETICKS
+#define TIMETICKS ASN_TIMETICKS
+#define STRING ASN_OCTET_STR
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* If ARRAY_SIZE is not available use a primitive substitution */
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+/*
+ * Define time function, it serves two purposes
+ * 1. Uses unint32_t for unix time and encapsulates
+ * sing extension issues in conversion from time_t
+ *
+ * 2. I could be replaced in unit test environment
+ */
+#ifndef ISIS_SNMP_HAVE_TIME_FUNC
+static uint32_t isis_snmp_time(void)
+{
+ return (uint32_t)time(NULL);
+}
+
+#endif
+
+/* ISIS-MIB instances. */
+static oid isis_oid[] = {ISIS_MIB};
+
+/* SNMP trap variable */
+static oid isis_snmp_trap_var[] = {ISIS_SNMP_TRAP_VAR};
+
+/* SNMP trap values (others are calculated on the fly */
+static oid isis_snmp_notifications[] = {ISIS_NOTIFICATIONS};
+static oid isis_snmp_trap_val_db_overload[] = {ISIS_NOTIFICATIONS,
+ ISIS_TRAP_DB_OVERLOAD};
+static oid isis_snmp_trap_val_lsp_exceed_max[] = {ISIS_NOTIFICATIONS,
+ ISIS_TRAP_LSP_EXCEED_MAX};
+static oid isis_snmp_trap_val_area_mismatch[] = {ISIS_NOTIFICATIONS,
+ ISIS_TRAP_AREA_MISMATCH};
+static oid isis_snmp_trap_val_lsp_error[] = {ISIS_NOTIFICATIONS,
+ ISIS_TRAP_LSP_ERROR};
+
+/*
+ * Trap vars under 'isisNotifications': note: we use full names of variables
+ * scalar index
+ */
+static oid isis_snmp_trap_data_var_sys_level_index[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_NOTIF_SYLELVELINDEX, 0};
+static oid isis_snmp_trap_data_var_circ_if_index[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_NOTIF_CIRCIFINDEX, 0};
+static oid isis_snmp_trap_data_var_pdu_lsp_id[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_LSPID, 0};
+static oid isis_snmp_trap_data_var_pdu_fragment[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_FRAGMENT, 0};
+static oid isis_snmp_trap_data_var_pdu_field_len[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_FIELDLEN, 0};
+static oid isis_snmp_trap_data_var_pdu_max_area_addr[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_MAXAREAADDR, 0};
+static oid isis_snmp_trap_data_var_pdu_proto_ver[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_PROTOVER, 0};
+static oid isis_snmp_trap_data_var_pdu_lsp_size[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_PDU_LSPSIZE, 0};
+static oid isis_snmp_trap_data_var_adj_state[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ADJ_STATE, 0};
+static oid isis_snmp_trap_data_var_error_offset[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ERROR_OFFSET, 0};
+static oid isis_snmp_trap_data_var_error_tlv_type[] = {
+ ISIS_MIB, ISIS_NOTIFICATION_ENTRY, ISIS_ERROR_TLVTYPE, 0};
+
+/*
+ * Other variables used by traps: note we use full names of variables and
+ * reserve space for index
+ */
+static oid isis_snmp_trap_data_var_sys_level_state[] = {
+ ISIS_MIB, ISIS_SYSLEVEL_ENTRY, ISIS_SYSLEVEL_STATE, 0};
+
+/* Throttle time values for traps */
+static time_t isis_snmp_trap_timestamp[ISIS_TRAP_LAST_TRAP]; /* ?? 1 */
+
+/* Max len of raw-pdu in traps */
+#define ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN (64)
+
+/*
+ * Just to save on typing we have a shortcut structure
+ * to specify mib layout as prefix/leaf combination
+ */
+#define ISIS_SNMP_PREF_LEN_MAX 10
+struct isis_var_prefix {
+ FindVarMethod *findVar;
+ uint8_t ivd_pref_len;
+ oid ivd_pref[ISIS_SNMP_PREF_LEN_MAX];
+};
+
+
+/* Find-val functions */
+static uint8_t *isis_snmp_find_sys_object(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_man_area(struct variable *, oid *, size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_area_addr(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_summ_addr(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_redistribute_addr(struct variable *, oid *,
+ size_t *, int, size_t *,
+ WriteMethod **);
+
+static uint8_t *isis_snmp_find_router(struct variable *, oid *, size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_sys_level(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_system_counter(struct variable *, oid *,
+ size_t *, int, size_t *,
+ WriteMethod **);
+
+static uint8_t *isis_snmp_find_next_circ_index(struct variable *, oid *,
+ size_t *, int, size_t *,
+ WriteMethod **);
+
+static uint8_t *isis_snmp_find_circ(struct variable *, oid *, size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_circ_level(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_circ_counter(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_isadj(struct variable *, oid *, size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_isadj_area(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_isadj_ipaddr(struct variable *, oid *, size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *isis_snmp_find_isadj_prot_supp(struct variable *, oid *,
+ size_t *, int, size_t *,
+ WriteMethod **);
+
+/*
+ * Just to save on typing we have a shortcut structure
+ * to specify mib layout, we populate the rest of the data
+ * during initialization
+ */
+#define ISIS_PREF_LEN_MAX (6)
+
+struct isis_func_to_prefix {
+ FindVarMethod *ihtp_func;
+ oid ihtp_pref_oid[ISIS_PREF_LEN_MAX];
+ uint8_t ihtp_pref_len;
+};
+
+static struct isis_func_to_prefix isis_func_to_prefix_arr[] = {
+ {isis_snmp_find_sys_object, {ISIS_SYS_OBJECT}, 3},
+ {isis_snmp_find_man_area, {ISIS_MANAREA_ADDRENTRY}, 4},
+ {isis_snmp_find_area_addr, {ISIS_AREA_ADDRENTRY}, 4},
+ {isis_snmp_find_summ_addr, {ISIS_SUMM_ADDRENTRY}, 4},
+ {isis_snmp_find_redistribute_addr, {ISIS_REDISTRIBUTE_ADDRENTRY}, 4},
+ {isis_snmp_find_router, {ISIS_ROUTER_ENTRY}, 4},
+ {isis_snmp_find_sys_level, {ISIS_SYSLEVEL_ENTRY}, 4},
+ {isis_snmp_find_system_counter, {ISIS_SYSTEM_COUNTER_ENTRY}, 4},
+ {isis_snmp_find_next_circ_index, {ISIS_CIRC}, 2},
+ {isis_snmp_find_circ, {ISIS_CIRC_ENTRY}, 4},
+ {isis_snmp_find_circ_level, {ISIS_CIRCLEVEL_ENTRY}, 4},
+ {isis_snmp_find_circ_counter, {ISIS_CIRC_COUNTER_ENTRY}, 4},
+ {isis_snmp_find_isadj, {ISIS_ISADJ_ENTRY}, 4},
+ {isis_snmp_find_isadj_area, {ISIS_ISADJAREA_ADDRENTRY}, 4},
+ {isis_snmp_find_isadj_ipaddr, {ISIS_ISADJIPADDR_ENTRY}, 4},
+ {isis_snmp_find_isadj_prot_supp, {ISIS_ISADJPROTSUPP_ENTRY}, 4},
+};
+static size_t isis_func_to_prefix_count = ARRAY_SIZE(isis_func_to_prefix_arr);
+
+static struct variable isis_var_arr[] = {
+ {ISIS_SYS_VERSION, INTEGER, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_LEVELTYPE, INTEGER, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_ID, STRING, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_MAXPATHSPLITS, UNSIGNED32, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_MAXLSPGENINT, UNSIGNED32, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_POLLESHELLORATE, UNSIGNED32, RONLY,
+ isis_snmp_find_sys_object},
+ {ISIS_SYS_WAITTIME, UNSIGNED32, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_ADMINSTATE, INTEGER, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_L2TOL1LEAKING, INTEGER, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_MAXAGE, UNSIGNED32, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_RECEIVELSPBUFFERSIZE, UNSIGNED32, RONLY,
+ isis_snmp_find_sys_object},
+ {ISIS_SYS_PROTSUPPORTED, STRING, RONLY, isis_snmp_find_sys_object},
+ {ISIS_SYS_NOTIFICATIONENABLE, INTEGER, RONLY,
+ isis_snmp_find_sys_object},
+ {ISIS_MANAREA_ADDREXISTSTATE, INTEGER, RONLY, isis_snmp_find_man_area},
+ {ISIS_AREA_ADDR, STRING, RONLY, isis_snmp_find_area_addr},
+ {ISIS_SUMM_ADDREXISTSTATE, INTEGER, RONLY, isis_snmp_find_summ_addr},
+ {ISIS_SUMM_ADDRMETRIC, UNSIGNED32, RONLY, isis_snmp_find_summ_addr},
+ {ISIS_SUMM_ADDRFULLMETRIC, UNSIGNED32, RONLY, isis_snmp_find_summ_addr},
+ {ISIS_REDISTRIBUTE_ADDREXISTSTATE, INTEGER, RONLY,
+ isis_snmp_find_redistribute_addr},
+ {ISIS_ROUTER_HOSTNAME, STRING, RONLY, isis_snmp_find_router},
+ {ISIS_ROUTER_ID, UNSIGNED32, RONLY, isis_snmp_find_router},
+ {ISIS_SYSLEVEL_ORIGLSPBUFFSIZE, UNSIGNED32, RONLY,
+ isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_MINLSPGENINT, UNSIGNED32, RONLY,
+ isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_STATE, INTEGER, RONLY, isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_SETOVERLOAD, INTEGER, RONLY, isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_SETOVERLOADUNTIL, UNSIGNED32, RONLY,
+ isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_METRICSTYLE, INTEGER, RONLY, isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_SPFCONSIDERS, INTEGER, RONLY, isis_snmp_find_sys_level},
+ {ISIS_SYSLEVEL_TEENABLED, INTEGER, RONLY, isis_snmp_find_sys_level},
+ {ISIS_SYSSTAT_CORRLSPS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_AUTHTYPEFAILS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_AUTHFAILS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_LSPDBASEOLOADS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_MANADDRDROPFROMAREAS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_SEQNUMSKIPS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_OWNLSPPURGES, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_IDFIELDLENMISMATCHES, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_PARTCHANGES, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_SPFRUNS, COUNTER32, RONLY, isis_snmp_find_system_counter},
+ {ISIS_SYSSTAT_LSPERRORS, COUNTER32, RONLY,
+ isis_snmp_find_system_counter},
+ {ISIS_NEXTCIRC_INDEX, UNSIGNED32, RONLY,
+ isis_snmp_find_next_circ_index},
+ {ISIS_CIRC_IFINDEX, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_ADMINSTATE, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_EXISTSTATE, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_TYPE, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_EXTDOMAIN, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_LEVELTYPE, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_PASSIVECIRCUIT, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_MESHGROUPENABLED, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_MESHGROUP, UNSIGNED32, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_SMALLHELLOS, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_LASTUPTIME, TIMESTAMP, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_3WAYENABLED, INTEGER, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRC_EXTENDEDCIRCID, UNSIGNED32, RONLY, isis_snmp_find_circ},
+ {ISIS_CIRCLEVEL_METRIC, UNSIGNED32, RONLY, isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_WIDEMETRIC, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_ISPRIORITY, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_IDOCTET, UNSIGNED32, RONLY, isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_ID, STRING, RONLY, isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_DESIS, STRING, RONLY, isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_HELLOMULTIPLIER, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_HELLOTIMER, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_DRHELLOTIMER, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_LSPTHROTTLE, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_MINLSPRETRANSINT, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_CSNPINTERVAL, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRCLEVEL_PARTSNPINTERVAL, UNSIGNED32, RONLY,
+ isis_snmp_find_circ_level},
+ {ISIS_CIRC_ADJCHANGES, COUNTER32, RONLY, isis_snmp_find_circ_counter},
+ {ISIS_CIRC_NUMADJ, UNSIGNED32, RONLY, isis_snmp_find_circ_counter},
+ {ISIS_CIRC_INITFAILS, COUNTER32, RONLY, isis_snmp_find_circ_counter},
+ {ISIS_CIRC_REJADJS, COUNTER32, RONLY, isis_snmp_find_circ_counter},
+ {ISIS_CIRC_IDFIELDLENMISMATCHES, COUNTER32, RONLY,
+ isis_snmp_find_circ_counter},
+ {ISIS_CIRC_MAXAREAADDRMISMATCHES, COUNTER32, RONLY,
+ isis_snmp_find_circ_counter},
+ {ISIS_CIRC_AUTHTYPEFAILS, COUNTER32, RONLY,
+ isis_snmp_find_circ_counter},
+ {ISIS_CIRC_AUTHFAILS, COUNTER32, RONLY, isis_snmp_find_circ_counter},
+ {ISIS_CIRC_LANDESISCHANGES, COUNTER32, RONLY,
+ isis_snmp_find_circ_counter},
+ {ISIS_ISADJ_STATE, INTEGER, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_3WAYSTATE, INTEGER, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_NEIGHSNPAADDRESS, STRING, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_NEIGHSYSTYPE, INTEGER, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_NEIGHSYSID, STRING, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_NBREXTENDEDCIRCID, UNSIGNED32, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_USAGE, INTEGER, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_HOLDTIMER, UNSIGNED32, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_NEIGHPRIORITY, UNSIGNED32, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJ_LASTUPTIME, TIMESTAMP, RONLY, isis_snmp_find_isadj},
+ {ISIS_ISADJAREA_ADDRESS, STRING, RONLY, isis_snmp_find_isadj_area},
+ {ISIS_ISADJIPADDR_TYPE, INTEGER, RONLY, isis_snmp_find_isadj_ipaddr},
+ {ISIS_ISADJIPADDR_ADDRESS, STRING, RONLY, isis_snmp_find_isadj_ipaddr},
+ {ISIS_ISADJPROTSUPP_PROTOCOL, INTEGER, RONLY,
+ isis_snmp_find_isadj_prot_supp},
+};
+
+static const size_t isis_var_count = ARRAY_SIZE(isis_var_arr);
+
+/* Minimal set of hard-coded data */
+#define ISIS_VERSION (1)
+
+/* If sys-id is not set use this value */
+static uint8_t isis_null_sysid[ISIS_SYS_ID_LEN];
+
+/* OSI addr-len */
+#define ISIS_SNMP_OSI_ADDR_LEN_MAX (20)
+
+/*
+ * The implementation has a fixed max-path splits value
+ * of 64 (see ISIS_MAX_PATH_SPLITS), the max mib value
+ * is 32.
+ *
+ * FIXME(aromanov): should we return 32 or 64?
+ */
+#define ISIS_SNMP_MAX_PATH_SPLITS (32)
+
+#define ISIS_SNMP_ADMIN_STATE_ON (1)
+
+#define ISIS_SNMP_ROW_STATUS_ACTIVE (1)
+
+#define ISIS_SNMP_LEVEL_STATE_OFF (1)
+#define ISIS_SNMP_LEVEL_STATE_ON (2)
+#define ISIS_SNMP_LEVEL_STATE_WAITING (3)
+#define ISIS_SNMP_LEVEL_STATE_OVERLOADED (4)
+
+#define ISIS_SNMP_TRUTH_VALUE_TRUE (1)
+#define ISIS_SNMP_TRUTH_VALUE_FALSE (2)
+
+#define ISIS_SNMP_METRIC_STYLE_NARROW (1)
+#define ISIS_SNMP_METRIC_STYLE_WIDE (2)
+#define ISIS_SNMP_METRIC_STYLE_BOTH (3)
+
+#define ISIS_SNMP_MESH_GROUP_INACTIVE (1)
+
+#define ISIS_SNMP_ADJ_STATE_DOWN (1)
+#define ISIS_SNMP_ADJ_STATE_INITIALIZING (2)
+#define ISIS_SNMP_ADJ_STATE_UP (3)
+#define ISIS_SNMP_ADJ_STATE_FAILED (4)
+
+#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1 (1)
+#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L2 (2)
+#define ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1_L2 (3)
+#define ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN (4)
+
+#define ISIS_SNMP_INET_TYPE_V4 (1)
+#define ISIS_SNMP_INET_TYPE_V6 (2)
+
+#define ISIS_SNMP_P2P_CIRCUIT (3)
+
+/* Protocols supported value */
+static uint8_t isis_snmp_protocols_supported = 0x7; /* All: iso, ipv4, ipv6 */
+
+/*
+ * Convenience function to move to the next circuit,
+ */
+static struct isis_circuit *isis_snmp_circuit_next(struct isis_circuit *circuit)
+{
+ uint32_t start;
+ uint32_t off;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
+
+ start = 1;
+
+ if (circuit != NULL)
+ start = circuit->snmp_id + 1;
+
+ for (off = start; off < SNMP_CIRCUITS_MAX; off++) {
+ circuit = isis->snmp_circuits[off];
+
+ if (circuit != NULL)
+ return circuit;
+ }
+
+ return NULL;
+}
+
+/*
+ * Convenience function to get the first matching level
+ */
+static int isis_snmp_circuit_get_level_lo(struct isis_circuit *circuit)
+{
+ if (circuit->is_type == IS_LEVEL_2)
+ return IS_LEVEL_2;
+
+ return IS_LEVEL_1;
+}
+
+/* Check level match */
+static int isis_snmp_get_level_match(int is_type, int level)
+{
+ if (is_type != IS_LEVEL_1 && is_type != IS_LEVEL_2
+ && is_type != IS_LEVEL_1_AND_2)
+ return 0;
+
+ if (level != IS_LEVEL_1 && level != IS_LEVEL_2)
+ return 0;
+
+
+ if (is_type == IS_LEVEL_1) {
+ if (level == IS_LEVEL_1)
+ return 1;
+
+ return 0;
+ }
+
+ if (is_type == IS_LEVEL_2) {
+ if (level == IS_LEVEL_2)
+ return 1;
+
+ return 0;
+ }
+
+ return 1;
+}
+/*
+ * Helper function to convert oid index representing
+ * octet-string index (e.g. isis-sys-id) to byte string
+ * representing the same index.
+ *
+ * Also we do not fail if idx is longer than max_len,
+ * so we can use the same function to check compound
+ * indexes.
+ */
+static int isis_snmp_conv_exact(uint8_t *buf, size_t max_len, size_t *out_len,
+ const oid *idx, size_t idx_len)
+{
+ size_t off;
+ size_t len;
+
+ /* Oid representation: length followed by bytes */
+ if (idx == NULL || idx_len == 0)
+ return 0;
+
+ len = idx[0];
+
+ if (len > max_len)
+ return 0;
+
+ if (idx_len < len + 1)
+ return 0;
+
+ for (off = 0; off < len; off++) {
+ if (idx[off + 1] > 0xff)
+ return 0;
+
+ buf[off] = (uint8_t)(idx[off + 1] & 0xff);
+ }
+
+ *out_len = len;
+
+ return 1;
+}
+
+static int isis_snmp_conv_next(uint8_t *buf, size_t max_len, size_t *out_len,
+ int *try_exact, const oid *idx, size_t idx_len)
+{
+ size_t off;
+ size_t len;
+ size_t cmp_len;
+
+ if (idx == NULL || idx_len == 0) {
+ *out_len = 0;
+ *try_exact = 1;
+ return 1;
+ }
+
+ len = idx[0];
+
+ if (len > max_len)
+ return 0;
+
+ cmp_len = len;
+
+ if ((idx_len - 1) < cmp_len)
+ cmp_len = idx_len - 1;
+
+ for (off = 0; off < cmp_len; off++) {
+ if (idx[off + 1] > 0xff) {
+ memset(buf + off, 0xff, len - off);
+ *out_len = len;
+ *try_exact = 1;
+ return 1;
+ }
+
+ buf[off] = (uint8_t)(idx[off + 1] & 0xff);
+ }
+
+ if (cmp_len < len)
+ memset(buf + cmp_len, 0, len - cmp_len);
+
+ *out_len = len;
+ *try_exact = cmp_len < len ? 1 : 0;
+ return 1;
+}
+
+/*
+ * Helper functions to find area address from snmp index
+ */
+static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len,
+ struct isis_area **ret_area,
+ struct area_addr **ret_addr)
+{
+ uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX];
+ size_t addr_len;
+ struct isis_area *area = NULL;
+ struct area_addr *addr = NULL;
+ struct listnode *addr_node;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return 0;
+
+ if (list_isempty(isis->area_list)) {
+ /* Area is not configured yet */
+ return 0;
+ }
+
+ area = listgetdata(listhead(isis->area_list));
+
+ int res = isis_snmp_conv_exact(cmp_buf, sizeof(cmp_buf), &addr_len,
+ oid_idx, oid_idx_len);
+
+
+ if (!res || addr_len == 0 || oid_idx_len != (addr_len + 1)) {
+ /* Bad conversion, empty address or extra oids at the end */
+ return 0;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(area->area_addrs, addr_node, addr)) {
+ if (addr->addr_len != addr_len)
+ continue;
+
+ if (memcmp(addr->area_addr, cmp_buf, addr_len) == 0) {
+ if (ret_area != 0)
+ *ret_area = area;
+
+ if (ret_addr != 0)
+ *ret_addr = addr;
+
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int isis_snmp_area_addr_lookup_next(oid *oid_idx, size_t oid_idx_len,
+ struct isis_area **ret_area,
+ struct area_addr **ret_addr)
+{
+ uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX];
+ size_t addr_len;
+ int try_exact = 0;
+ struct isis_area *found_area = NULL;
+ struct isis_area *area = NULL;
+ struct area_addr *found_addr = NULL;
+ struct area_addr *addr = NULL;
+ struct listnode *addr_node;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return 0;
+
+ if (list_isempty(isis->area_list)) {
+ /* Area is not configured yet */
+ return 0;
+ }
+
+ area = listgetdata(listhead(isis->area_list));
+
+ int res = isis_snmp_conv_next(cmp_buf, sizeof(cmp_buf), &addr_len,
+ &try_exact, oid_idx, oid_idx_len);
+
+ if (!res)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS_RO(area->area_addrs, addr_node, addr)) {
+ if (addr->addr_len < addr_len)
+ continue;
+
+ if (addr->addr_len == addr_len) {
+ if (addr_len == 0)
+ continue;
+
+ res = memcmp(addr->area_addr, cmp_buf, addr_len);
+
+ if (res < 0)
+ continue;
+
+ if (res == 0 && addr->addr_len == addr_len) {
+ if (try_exact) {
+ /*
+ * This is the best match no point
+ * to look further
+ */
+ found_area = area;
+ found_addr = addr;
+ break;
+ }
+ continue;
+ }
+ }
+
+ if (found_addr == NULL || addr->addr_len < found_addr->addr_len
+ || (addr->addr_len == found_addr->addr_len
+ && memcmp(addr->area_addr, found_addr->area_addr,
+ addr->addr_len)
+ < 0)) {
+ found_area = area;
+ found_addr = addr;
+ }
+ }
+
+ if (found_area == NULL)
+ return 0;
+
+ if (ret_area != 0)
+ *ret_area = found_area;
+
+ if (ret_addr != 0)
+ *ret_addr = found_addr;
+
+ return 1;
+}
+
+/*
+ * Helper functions to find circuit from
+ * snmp index
+ */
+static int isis_snmp_circuit_lookup_exact(oid *oid_idx, size_t oid_idx_len,
+ struct isis_circuit **ret_circuit)
+{
+ struct isis_circuit *circuit;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return 0;
+
+ if (oid_idx == NULL || oid_idx_len < 1
+ || oid_idx[0] > SNMP_CIRCUITS_MAX)
+ return 0;
+
+ circuit = isis->snmp_circuits[oid_idx[0]];
+ if (circuit == NULL)
+ return 0;
+
+ if (ret_circuit != NULL)
+ *ret_circuit = circuit;
+
+ return 1;
+}
+
+static int isis_snmp_circuit_lookup_next(oid *oid_idx, size_t oid_idx_len,
+ struct isis_circuit **ret_circuit)
+{
+ oid off;
+ oid start;
+ struct isis_circuit *circuit;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return 0;
+
+ start = 0;
+
+ if (oid_idx != NULL && oid_idx_len != 0) {
+ if (oid_idx[0] > SNMP_CIRCUITS_MAX)
+ return 0;
+
+ start = oid_idx[0];
+ }
+
+ for (off = start; off < SNMP_CIRCUITS_MAX; ++off) {
+ circuit = isis->snmp_circuits[off];
+
+ if (circuit != NULL && off > start) {
+ if (ret_circuit != NULL)
+ *ret_circuit = circuit;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Helper functions to find circuit level
+ * combination from snmp index
+ */
+static int isis_snmp_circuit_level_lookup_exact(
+ oid *oid_idx, size_t oid_idx_len, int check_match,
+ struct isis_circuit **ret_circuit, int *ret_level)
+{
+ int level;
+ int res;
+ struct isis_circuit *circuit;
+
+ /* Minor optimization: check level first */
+ if (oid_idx == NULL || oid_idx_len < 2)
+ return 0;
+
+ if (oid_idx[1] < IS_LEVEL_1 || oid_idx[1] > IS_LEVEL_2)
+ return 0;
+
+ level = (int)oid_idx[1];
+
+ res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len, &circuit);
+
+ if (!res)
+ return 0;
+
+ if (check_match && !isis_snmp_get_level_match(circuit->is_type, level))
+ return 0;
+
+ if (ret_circuit != NULL)
+ *ret_circuit = circuit;
+
+ if (ret_level != NULL)
+ *ret_level = level;
+
+ return 1;
+}
+
+static int isis_snmp_circuit_level_lookup_next(
+ oid *oid_idx, size_t oid_idx_len, int check_match,
+ struct isis_circuit **ret_circuit, int *ret_level)
+{
+ oid off;
+ oid start;
+ struct isis_circuit *circuit;
+ int level;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return 0;
+
+ start = 0;
+
+ if (oid_idx != NULL && oid_idx_len != 0) {
+ if (oid_idx[0] > SNMP_CIRCUITS_MAX)
+ return 0;
+
+ start = oid_idx[0];
+ }
+
+ for (off = start; off < SNMP_CIRCUITS_MAX; off++) {
+ circuit = isis->snmp_circuits[off];
+
+ if (circuit == NULL)
+ continue;
+
+ if (off > start || oid_idx_len < 2) {
+ /* Found and can use level 1 */
+ level = IS_LEVEL_1;
+ break;
+ }
+
+ /* We have to check level specified by index */
+ if (oid_idx[1] < IS_LEVEL_1) {
+ level = IS_LEVEL_1;
+ break;
+ }
+
+ if (oid_idx[1] < IS_LEVEL_2) {
+ level = IS_LEVEL_2;
+ break;
+ }
+
+ /* Try next */
+ circuit = NULL;
+ }
+
+ if (circuit == NULL)
+ return 0;
+
+ if (check_match
+ && !isis_snmp_get_level_match(circuit->is_type, level)) {
+ if (level == IS_LEVEL_1) {
+ /*
+ * We can simply advance level because
+ * at least one level should match
+ */
+ level = IS_LEVEL_2;
+ } else {
+ /* We have to move to the next circuit */
+ circuit = isis_snmp_circuit_next(circuit);
+ if (circuit == NULL)
+ return 0;
+
+ level = isis_snmp_circuit_get_level_lo(circuit);
+ }
+ }
+
+ if (ret_circuit != NULL)
+ *ret_circuit = circuit;
+
+ if (ret_level != NULL)
+ *ret_level = level;
+
+ return 1;
+}
+
+/*
+ * Helper functions to find adjacency
+ * from snmp index.
+ *
+ * We have 4 tables related to adjacency
+ * looking up adjacency is quite expensive
+ * in case of bcast interfaces.
+ *
+ * It is pain to have 4 very similar functions
+ * hence we pass in and out additional data
+ * we are looking for.
+ *
+ * Note: we use data-len value to distinguish
+ * between ipv4 and ipv6 addresses
+ */
+#define ISIS_SNMP_ADJ_DATA_NONE (1)
+#define ISIS_SNMP_ADJ_DATA_AREA_ADDR (2)
+#define ISIS_SNMP_ADJ_DATA_IP_ADDR (3)
+#define ISIS_SNMP_ADJ_DATA_PROTO (4)
+
+/*
+ * Helper function to process data associated
+ * with adjacency
+ */
+static int isis_snmp_adj_helper(struct isis_adjacency *adj, int data_id,
+ oid data_off, uint8_t **ret_data,
+ size_t *ret_data_len)
+{
+ uint8_t *data = NULL;
+ size_t data_len = 0;
+
+ switch (data_id) {
+ case ISIS_SNMP_ADJ_DATA_NONE:
+ break;
+
+ case ISIS_SNMP_ADJ_DATA_AREA_ADDR:
+ if (data_off >= adj->area_address_count)
+ return 0;
+
+ data = adj->area_addresses[data_off].area_addr;
+ data_len = adj->area_addresses[data_off].addr_len;
+ break;
+
+ case ISIS_SNMP_ADJ_DATA_IP_ADDR:
+ if (data_off
+ >= (adj->ipv4_address_count + adj->ipv6_address_count))
+ return 0;
+
+ if (data_off >= adj->ipv4_address_count) {
+ data = (uint8_t *)&adj->ipv6_addresses
+ [data_off - adj->ipv4_address_count];
+ data_len = sizeof(adj->ipv6_addresses[0]);
+ } else {
+ data = (uint8_t *)&adj->ipv4_addresses[data_off];
+ data_len = sizeof(adj->ipv4_addresses[0]);
+ }
+
+ break;
+
+
+ case ISIS_SNMP_ADJ_DATA_PROTO:
+ if (data_off >= adj->nlpids.count)
+ return 0;
+
+ data = &adj->nlpids.nlpids[data_off];
+ data_len = sizeof(adj->nlpids.nlpids[0]);
+ break;
+
+ default:
+ assert(0);
+ return 0;
+ }
+
+ if (ret_data != NULL)
+ *ret_data = data;
+
+ if (ret_data_len != NULL)
+ *ret_data_len = data_len;
+
+ return 1;
+}
+
+static int isis_snmp_adj_lookup_exact(oid *oid_idx, size_t oid_idx_len,
+ int data_id,
+ struct isis_adjacency **ret_adj,
+ oid *ret_data_idx, uint8_t **ret_data,
+ size_t *ret_data_len)
+{
+ int res;
+ struct listnode *node;
+ struct isis_circuit *circuit;
+ struct isis_adjacency *adj;
+ struct isis_adjacency *tmp_adj;
+ oid adj_idx;
+ oid data_off;
+ uint8_t *data;
+ size_t data_len;
+
+ res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len, &circuit);
+
+ if (!res)
+ return 0;
+
+ if (oid_idx == NULL || oid_idx_len < 2
+ || (data_id != ISIS_SNMP_ADJ_DATA_NONE && oid_idx_len < 3))
+ return 0;
+
+ adj_idx = oid_idx[1];
+
+ if (data_id != ISIS_SNMP_ADJ_DATA_NONE) {
+ if (oid_idx[2] == 0)
+ return 0;
+
+ data_off = oid_idx[2] - 1;
+ } else {
+ /*
+ * Data-off is not used if data-id is none
+ * but we set it just for consistency
+ */
+ data_off = 0;
+ }
+
+ adj = NULL;
+ data = NULL;
+ data_len = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(circuit->snmp_adj_list, node, tmp_adj)) {
+ if (tmp_adj->snmp_idx > adj_idx) {
+ /*
+ * Adjacencies are ordered in the list
+ * no point to look further
+ */
+ break;
+ }
+
+ if (tmp_adj->snmp_idx == adj_idx) {
+ res = isis_snmp_adj_helper(tmp_adj, data_id, data_off,
+ &data, &data_len);
+ if (res)
+ adj = tmp_adj;
+
+ break;
+ }
+ }
+
+ if (adj == NULL)
+ return 0;
+
+ if (ret_adj != NULL)
+ *ret_adj = adj;
+
+ if (ret_data_idx != NULL)
+ *ret_data_idx = data_off + 1;
+
+ if (ret_data)
+ *ret_data = data;
+
+ if (ret_data_len)
+ *ret_data_len = data_len;
+
+ return 1;
+}
+
+static int isis_snmp_adj_lookup_next(oid *oid_idx, size_t oid_idx_len,
+ int data_id,
+ struct isis_adjacency **ret_adj,
+ oid *ret_data_idx, uint8_t **ret_data,
+ size_t *ret_data_len)
+{
+ struct listnode *node;
+ struct isis_circuit *circuit;
+ struct isis_adjacency *adj;
+ struct isis_adjacency *tmp_adj;
+ oid circ_idx;
+ oid adj_idx;
+ oid data_idx;
+ uint8_t *data;
+ size_t data_len;
+
+ adj = NULL;
+ data = NULL;
+ data_len = 0;
+
+ /*
+ * Note: we rely on the fact that data indexes are consequtive
+ * starting from 1
+ */
+
+ if (oid_idx == 0 || oid_idx_len == 0) {
+ circ_idx = 0;
+ adj_idx = 0;
+ data_idx = 0;
+ } else if (oid_idx_len == 1) {
+ circ_idx = oid_idx[0];
+ adj_idx = 0;
+ data_idx = 0;
+ } else if (oid_idx_len == 2) {
+ circ_idx = oid_idx[0];
+ adj_idx = oid_idx[1];
+ data_idx = 0;
+ } else {
+ circ_idx = oid_idx[0];
+ adj_idx = oid_idx[1];
+
+ if (data_id == ISIS_SNMP_ADJ_DATA_NONE)
+ data_idx = 0;
+ else
+ data_idx = oid_idx[2];
+ }
+
+ if (!isis_snmp_circuit_lookup_exact(&circ_idx, 1, &circuit)
+ && !isis_snmp_circuit_lookup_next(&circ_idx, 1, &circuit))
+ /* No circuit */
+ return 0;
+
+ if (circuit->snmp_id != circ_idx) {
+ /* Match is not exact */
+ circ_idx = 0;
+ adj_idx = 0;
+ data_idx = 0;
+ }
+
+ /*
+ * Note: the simple loop below will work in all cases
+ */
+ while (circuit != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(circuit->snmp_adj_list, node,
+ tmp_adj)) {
+ if (tmp_adj->snmp_idx < adj_idx)
+ continue;
+
+ if (tmp_adj->snmp_idx == adj_idx
+ && data_id == ISIS_SNMP_ADJ_DATA_NONE)
+ continue;
+
+ if (adj_idx != 0 && tmp_adj->snmp_idx > adj_idx)
+ data_idx = 0;
+
+ if (isis_snmp_adj_helper(tmp_adj, data_id, data_idx,
+ &data, &data_len)) {
+ adj = tmp_adj;
+ break;
+ }
+ }
+
+ if (adj != NULL)
+ break;
+
+ circuit = isis_snmp_circuit_next(circuit);
+ circ_idx = 0;
+ adj_idx = 0;
+ data_idx = 0;
+ }
+
+ if (adj == NULL)
+ return 0;
+
+ if (ret_adj != NULL)
+ *ret_adj = adj;
+
+ if (ret_data_idx != 0) {
+ if (data_id == ISIS_SNMP_ADJ_DATA_NONE)
+ /*
+ * Value does not matter but let us set
+ * it to zero for consistency
+ */
+ *ret_data_idx = 0;
+ else
+ *ret_data_idx = data_idx + 1;
+ }
+
+ if (ret_data != 0)
+ *ret_data = data;
+
+ if (ret_data_len != 0)
+ *ret_data_len = data_len;
+
+ return 1;
+}
+
+static uint8_t *isis_snmp_find_sys_object(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct isis_area *area = NULL;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
+
+ if (!list_isempty(isis->area_list))
+ area = listgetdata(listhead(isis->area_list));
+
+ /* Check whether the instance identifier is valid */
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ switch (v->magic) {
+ case ISIS_SYS_VERSION:
+ return SNMP_INTEGER(ISIS_VERSION);
+
+ case ISIS_SYS_LEVELTYPE:
+ /*
+ * If we do not have areas use 1&2 otherwise use settings
+ * from the first area in the list
+ */
+ if (area == NULL)
+ return SNMP_INTEGER(IS_LEVEL_1_AND_2);
+
+ return SNMP_INTEGER(area->is_type);
+
+ case ISIS_SYS_ID:
+ if (!isis->sysid_set) {
+ *var_len = ISIS_SYS_ID_LEN;
+ return isis_null_sysid;
+ }
+
+ *var_len = ISIS_SYS_ID_LEN;
+ return isis->sysid;
+
+ case ISIS_SYS_MAXPATHSPLITS:
+ return SNMP_INTEGER(ISIS_SNMP_MAX_PATH_SPLITS);
+
+ case ISIS_SYS_MAXLSPGENINT:
+ return SNMP_INTEGER(DEFAULT_MAX_LSP_GEN_INTERVAL);
+
+ case ISIS_SYS_POLLESHELLORATE:
+ return SNMP_INTEGER(DEFAULT_HELLO_INTERVAL);
+
+ case ISIS_SYS_WAITTIME:
+ /* Note: it seems that we have same fixed delay time */
+ return SNMP_INTEGER(DEFAULT_MIN_LSP_GEN_INTERVAL);
+
+ case ISIS_SYS_ADMINSTATE:
+ /* If daemon is running it admin state is on */
+ return SNMP_INTEGER(ISIS_SNMP_ADMIN_STATE_ON);
+
+
+ case ISIS_SYS_L2TOL1LEAKING:
+ /* We do not allow l2-to-l1 leaking */
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ case ISIS_SYS_MAXAGE:
+ return SNMP_INTEGER(MAX_AGE);
+
+ case ISIS_SYS_RECEIVELSPBUFFERSIZE:
+ if (area == NULL)
+ return SNMP_INTEGER(DEFAULT_LSP_MTU);
+
+ return SNMP_INTEGER(area->lsp_mtu);
+
+ case ISIS_SYS_PROTSUPPORTED:
+ *var_len = 1;
+ return &isis_snmp_protocols_supported;
+
+ case ISIS_SYS_NOTIFICATIONENABLE:
+ if (isis->snmp_notifications)
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+
+static uint8_t *isis_snmp_find_man_area(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ int res;
+ struct area_addr *area_addr = NULL;
+ oid *oid_idx;
+ size_t oid_idx_len;
+ size_t off = 0;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+
+ if (exact) {
+ res = isis_snmp_area_addr_lookup_exact(oid_idx, oid_idx_len,
+ NULL, &area_addr);
+
+ if (!res)
+ return NULL;
+
+ } else {
+ res = isis_snmp_area_addr_lookup_next(oid_idx, oid_idx_len,
+ NULL, &area_addr);
+
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = area_addr->addr_len;
+
+ for (off = 0; off < area_addr->addr_len; off++)
+ name[v->namelen + 1 + off] = area_addr->area_addr[off];
+
+ *length = v->namelen + 1 + area_addr->addr_len;
+ }
+
+ switch (v->magic) {
+ case ISIS_MANAREA_ADDREXISTSTATE:
+ return SNMP_INTEGER(ISIS_SNMP_ROW_STATUS_ACTIVE);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_area_addr(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /*
+ * Area addresses in sense of addresses reported by L1 lsps
+ * are not supported yet.
+ */
+ (void)v;
+ (void)name;
+ (void)length;
+ (void)exact;
+ (void)var_len;
+
+
+ *write_method = NULL;
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_summ_addr(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /*
+ * So far there is no way to set summary table values through cli
+ * and snmp operations are read-only, hence there are no entries
+ */
+ (void)v;
+ (void)name;
+ (void)length;
+ (void)exact;
+ (void)var_len;
+ *write_method = NULL;
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_redistribute_addr(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /*
+ * It is not clear at the point whether redist code in isis is actually
+ * used for now we will consider that entries are not present
+ */
+ (void)v;
+ (void)name;
+ (void)length;
+ (void)exact;
+ (void)var_len;
+ *write_method = NULL;
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_router(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ uint8_t cmp_buf[ISIS_SYS_ID_LEN];
+ size_t cmp_len;
+ int try_exact;
+ int cmp_level;
+ int res;
+ struct isis_dynhn *dyn = NULL;
+ oid *oid_idx;
+ size_t oid_idx_len;
+ size_t off = 0;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+
+ if (exact) {
+ res = isis_snmp_conv_exact(cmp_buf, sizeof(cmp_buf), &cmp_len,
+ oid_idx, oid_idx_len);
+
+ if (!res || cmp_len != ISIS_SYS_ID_LEN
+ || oid_idx_len != (cmp_len + 2))
+ /*
+ * Bad conversion, or bad length,
+ * or extra oids at the end
+ */
+ return NULL;
+
+ if (oid_idx[ISIS_SYS_ID_LEN + 1] < IS_LEVEL_1
+ || oid_idx[ISIS_SYS_ID_LEN + 1] > IS_LEVEL_2)
+ /* Level part of the index is out of range */
+ return NULL;
+
+ cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1];
+
+ dyn = dynhn_find_by_id(cmp_buf);
+
+ if (dyn == NULL || dyn->level != cmp_level)
+ return NULL;
+
+ switch (v->magic) {
+ case ISIS_ROUTER_HOSTNAME:
+ *var_len = strlen(dyn->hostname);
+ return (uint8_t *)dyn->hostname;
+
+ case ISIS_ROUTER_ID:
+ /* It seems that we do no know router-id in lsps */
+ return SNMP_INTEGER(0);
+
+ default:
+ break;
+ }
+
+ return NULL;
+ }
+
+ res = isis_snmp_conv_next(cmp_buf, sizeof(cmp_buf), &cmp_len,
+ &try_exact, oid_idx, oid_idx_len);
+
+
+ if (!res)
+ /* Bad conversion */
+ return NULL;
+
+ if (cmp_len != ISIS_SYS_ID_LEN) {
+ /* We do not have valid index oids */
+ memset(cmp_buf, 0, sizeof(cmp_buf));
+ cmp_level = 0;
+ } else if (try_exact)
+ /*
+ * We have no valid level index.
+ * Let start from non-existing level 0 and
+ * hence not need to do exact match
+ */
+ cmp_level = 0;
+ else if (oid_idx_len < (ISIS_SYS_ID_LEN + 2))
+ cmp_level = 0;
+ else if (oid_idx[ISIS_SYS_ID_LEN + 1] <= IS_LEVEL_2)
+ cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1];
+ else
+ /*
+ * Any value greater than 2 will have the same result
+ * but we can have integer overflows, hence 3 is a reasonable
+ * choice
+ */
+ cmp_level = (int)(IS_LEVEL_2 + 1);
+
+ dyn = dynhn_snmp_next(cmp_buf, cmp_level);
+
+ if (dyn == NULL)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = ISIS_SYS_ID_LEN;
+
+ for (off = 0; off < ISIS_SYS_ID_LEN; off++)
+ name[v->namelen + 1 + off] = dyn->id[off];
+
+ name[v->namelen + 1 + ISIS_SYS_ID_LEN] = (oid)dyn->level;
+
+ /* Set length */
+ *length = v->namelen + 1 + ISIS_SYS_ID_LEN + 1;
+
+ switch (v->magic) {
+ case ISIS_ROUTER_HOSTNAME:
+ *var_len = strlen(dyn->hostname);
+ return (uint8_t *)dyn->hostname;
+
+ case ISIS_ROUTER_ID:
+ /* It seems that we do no know router-id in lsps */
+ return SNMP_INTEGER(0);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_sys_level(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int level;
+ int level_match;
+ struct isis_area *area = NULL;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+
+ if (exact) {
+ if (oid_idx == NULL || oid_idx_len != 1)
+ return NULL;
+
+ if (oid_idx[0] == IS_LEVEL_1)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] == IS_LEVEL_2)
+ level = IS_LEVEL_2;
+ else
+ return NULL;
+
+ } else {
+ if (oid_idx == NULL)
+ level = IS_LEVEL_1;
+ else if (oid_idx_len == 0)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] < IS_LEVEL_1)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] < IS_LEVEL_2)
+ level = IS_LEVEL_2;
+ else
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = level;
+
+ /* Set length */
+ *length = v->namelen + 1;
+ }
+
+ area = NULL;
+
+ if (!list_isempty(isis->area_list))
+ area = listgetdata(listhead(isis->area_list));
+
+ level_match = 0;
+
+ if (area != NULL)
+ level_match = isis_snmp_get_level_match(area->is_type, level);
+
+ switch (v->magic) {
+ case ISIS_SYSLEVEL_ORIGLSPBUFFSIZE:
+ if (level_match)
+ return SNMP_INTEGER(area->lsp_mtu);
+
+ return SNMP_INTEGER(DEFAULT_LSP_MTU);
+
+ case ISIS_SYSLEVEL_MINLSPGENINT:
+ if (level_match)
+ return SNMP_INTEGER(area->lsp_gen_interval[level - 1]);
+ else
+ return SNMP_INTEGER(DEFAULT_MIN_LSP_GEN_INTERVAL);
+
+ case ISIS_SYSLEVEL_STATE:
+ if (level_match) {
+ if (area->overload_bit)
+ return SNMP_INTEGER(
+ ISIS_SNMP_LEVEL_STATE_OVERLOADED);
+
+ return SNMP_INTEGER(ISIS_SNMP_LEVEL_STATE_ON);
+ }
+ return SNMP_INTEGER(ISIS_SNMP_LEVEL_STATE_OFF);
+
+ case ISIS_SYSLEVEL_SETOVERLOAD:
+ if (level_match && area->overload_bit)
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ case ISIS_SYSLEVEL_SETOVERLOADUNTIL:
+ /* We do not have automatic cleanup of overload bit */
+ return SNMP_INTEGER(0);
+
+ case ISIS_SYSLEVEL_METRICSTYLE:
+ if (level_match) {
+ if (area->newmetric && area->oldmetric)
+ return SNMP_INTEGER(
+ ISIS_SNMP_METRIC_STYLE_BOTH);
+
+ if (area->newmetric)
+ return SNMP_INTEGER(
+ ISIS_SNMP_METRIC_STYLE_WIDE);
+
+ return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_NARROW);
+ }
+ return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_NARROW);
+
+ case ISIS_SYSLEVEL_SPFCONSIDERS:
+ return SNMP_INTEGER(ISIS_SNMP_METRIC_STYLE_BOTH);
+
+ case ISIS_SYSLEVEL_TEENABLED:
+ if (level_match && IS_MPLS_TE(area->mta))
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_system_counter(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int level;
+ int level_match;
+ struct isis_area *area = NULL;
+ uint32_t val;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+
+ if (exact) {
+ if (oid_idx == NULL || oid_idx_len != 1)
+ return 0;
+
+ if (oid_idx[0] == IS_LEVEL_1)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] == IS_LEVEL_2)
+ level = IS_LEVEL_2;
+ else
+ return NULL;
+
+ } else {
+ if (oid_idx == NULL)
+ level = IS_LEVEL_1;
+ else if (oid_idx_len == 0)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] < IS_LEVEL_1)
+ level = IS_LEVEL_1;
+ else if (oid_idx[0] < IS_LEVEL_2)
+ level = IS_LEVEL_2;
+ else
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = level;
+
+ /* Set length */
+ *length = v->namelen + 1;
+ }
+
+ area = NULL;
+
+ if (!list_isempty(isis->area_list))
+ area = listgetdata(listhead(isis->area_list));
+
+ level_match = 0;
+
+ if (area != NULL)
+ level_match = isis_snmp_get_level_match(area->is_type, level);
+
+ if (!level_match)
+ /* If level does not match all counters are zeros */
+ return SNMP_INTEGER(0);
+
+ val = 0;
+
+ switch (v->magic) {
+ case ISIS_SYSSTAT_CORRLSPS:
+ val = 0;
+ break;
+
+ case ISIS_SYSSTAT_AUTHTYPEFAILS:
+ val = (uint32_t)area->auth_type_failures[level - 1];
+ break;
+
+ case ISIS_SYSSTAT_AUTHFAILS:
+ val = (uint32_t)area->auth_failures[level - 1];
+ break;
+
+ case ISIS_SYSSTAT_LSPDBASEOLOADS:
+ val = area->overload_counter;
+ break;
+
+ case ISIS_SYSSTAT_MANADDRDROPFROMAREAS:
+ /* We do not support manual addresses */
+ val = 0;
+ break;
+
+ case ISIS_SYSSTAT_ATTMPTTOEXMAXSEQNUMS:
+ val = area->lsp_exceeded_max_counter;
+ break;
+
+ case ISIS_SYSSTAT_SEQNUMSKIPS:
+ val = area->lsp_seqno_skipped_counter;
+ break;
+
+ case ISIS_SYSSTAT_OWNLSPPURGES:
+ if (!area->purge_originator)
+ val = 0;
+ else
+ val = area->lsp_purge_count[level - 1];
+ break;
+
+ case ISIS_SYSSTAT_IDFIELDLENMISMATCHES:
+ val = (uint32_t)area->id_len_mismatches[level - 1];
+ break;
+
+ case ISIS_SYSSTAT_PARTCHANGES:
+ /* Not supported */
+ val = 0;
+ break;
+
+ case ISIS_SYSSTAT_SPFRUNS:
+ val = (uint32_t)area->spf_run_count[level - 1];
+ break;
+
+ case ISIS_SYSSTAT_LSPERRORS:
+ val = (uint32_t)area->lsp_error_counter[level - 1];
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return SNMP_INTEGER(val);
+}
+
+static uint8_t *isis_snmp_find_next_circ_index(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Check whether the instance identifier is valid */
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ switch (v->magic) {
+ case ISIS_NEXTCIRC_INDEX:
+ /*
+ * We do not support circuit creation through snmp
+ */
+ return SNMP_INTEGER(0);
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name,
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index is circuit-id: 1-255 */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ struct isis_circuit *circuit;
+ uint32_t up_ticks;
+ uint32_t delta_ticks;
+ uint32_t now_time;
+ int res;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_circuit_lookup_exact(oid_idx, oid_idx_len,
+ &circuit);
+
+ if (!res || oid_idx_len != 1)
+ return NULL;
+
+ } else {
+ res = isis_snmp_circuit_lookup_next(oid_idx, oid_idx_len,
+ &circuit);
+
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = circuit->snmp_id;
+
+ /* Set length */
+ *length = v->namelen + 1;
+ }
+
+ switch (v->magic) {
+ case ISIS_CIRC_IFINDEX:
+ if (circuit->interface == 0)
+ return SNMP_INTEGER(0);
+
+ return SNMP_INTEGER(circuit->interface->ifindex);
+
+ case ISIS_CIRC_ADMINSTATE:
+ return SNMP_INTEGER(ISIS_SNMP_ADMIN_STATE_ON);
+
+ case ISIS_CIRC_EXISTSTATE:
+ return SNMP_INTEGER(ISIS_SNMP_ROW_STATUS_ACTIVE);
+
+ case ISIS_CIRC_TYPE:
+ /*
+ * Note: values do not match 100%:
+ *
+ * 1. From isis_circuit.h:
+ * CIRCUIT_T_UNKNOWN 0
+ * CIRCUIT_T_BROADCAST 1
+ * CIRCUIT_T_P2P 2
+ * CIRCUIT_T_LOOPBACK 3
+ *
+ * 2. From rfc:
+ * broadcast(1),
+ * ptToPt(2),
+ * staticIn(3),
+ * staticOut(4),
+ */
+
+ return SNMP_INTEGER(circuit->circ_type);
+
+ case ISIS_CIRC_EXTDOMAIN:
+ if (circuit->ext_domain)
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ case ISIS_CIRC_LEVELTYPE:
+ return SNMP_INTEGER(circuit->is_type);
+
+ case ISIS_CIRC_PASSIVECIRCUIT:
+ if (circuit->is_passive)
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ case ISIS_CIRC_MESHGROUPENABLED:
+ /* Not supported */
+ return SNMP_INTEGER(ISIS_SNMP_MESH_GROUP_INACTIVE);
+
+ case ISIS_CIRC_MESHGROUP:
+ /* Not supported */
+ return SNMP_INTEGER(0);
+
+ case ISIS_CIRC_SMALLHELLOS:
+ /*
+ * return false if lan hellos must be padded
+ */
+ if (circuit->pad_hellos)
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE);
+
+ case ISIS_CIRC_LASTUPTIME:
+ if (circuit->last_uptime == 0)
+ return SNMP_INTEGER(0);
+
+ up_ticks = (uint32_t)netsnmp_get_agent_uptime();
+ now_time = isis_snmp_time();
+
+ if (circuit->last_uptime >= now_time)
+ return SNMP_INTEGER(up_ticks);
+
+ delta_ticks = (now_time - circuit->last_uptime) * 10;
+
+ if (up_ticks < delta_ticks)
+ return SNMP_INTEGER(up_ticks);
+
+ return SNMP_INTEGER(up_ticks - delta_ticks);
+
+ case ISIS_CIRC_3WAYENABLED:
+ /* Not supported */
+ return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE);
+
+ case ISIS_CIRC_EXTENDEDCIRCID:
+ /* Used for 3-way hand shake only */
+ return SNMP_INTEGER(0);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_circ_level(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ static uint8_t circuit_id_val[ISIS_SYS_ID_LEN + 1];
+ /* Index is circuit-id: 1-255 + level: 1-2 */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ struct isis_circuit *circuit;
+ int level;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_circuit_level_lookup_exact(oid_idx, oid_idx_len,
+ 1, &circuit, &level);
+
+ if (!res || oid_idx_len != 2)
+ return NULL;
+
+ } else {
+ res = isis_snmp_circuit_level_lookup_next(oid_idx, oid_idx_len,
+ 1, &circuit, &level);
+
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = circuit->snmp_id;
+ name[v->namelen + 1] = level;
+
+ /* Set length */
+ *length = v->namelen + 2;
+ }
+
+ switch (v->magic) {
+ case ISIS_CIRCLEVEL_METRIC:
+ return SNMP_INTEGER(circuit->metric[level - 1]);
+
+ case ISIS_CIRCLEVEL_WIDEMETRIC:
+ if (circuit->area == NULL || !circuit->area->newmetric) {
+ /* What should we do if wide metric is not supported? */
+ return SNMP_INTEGER(0);
+ }
+ return SNMP_INTEGER(circuit->te_metric[level - 1]);
+
+ case ISIS_CIRCLEVEL_ISPRIORITY:
+ return SNMP_INTEGER(circuit->priority[level - 1]);
+
+ case ISIS_CIRCLEVEL_IDOCTET:
+ return SNMP_INTEGER(circuit->circuit_id);
+
+ case ISIS_CIRCLEVEL_ID:
+ if (circuit->circ_type != CIRCUIT_T_P2P) {
+ /*
+ * Unless it is point-to-point circuit, the value is and
+ * empty octet string
+ */
+ *var_len = 0;
+ return circuit_id_val;
+ }
+
+ /* !!!!!! Circuit-id is zero for p2p links */
+ if (circuit->u.p2p.neighbor == NULL
+ || circuit->u.p2p.neighbor->adj_state != ISIS_ADJ_UP) {
+ /* No adjacency or adjacency not fully up yet */
+ memcpy(circuit_id_val, isis->sysid, ISIS_SYS_ID_LEN);
+ circuit_id_val[ISIS_SYS_ID_LEN] = circuit->circuit_id;
+ *var_len = ISIS_SYS_ID_LEN + 1;
+ return circuit_id_val;
+ }
+
+ /* Adjacency fully-up */
+ memcpy(circuit_id_val, circuit->u.p2p.neighbor->sysid,
+ ISIS_SYS_ID_LEN);
+ circuit_id_val[ISIS_SYS_ID_LEN] = 0;
+ *var_len = ISIS_SYS_ID_LEN + 1;
+ return circuit_id_val;
+
+ case ISIS_CIRCLEVEL_DESIS:
+ if (circuit->circ_type != CIRCUIT_T_BROADCAST
+ || !circuit->u.bc.is_dr[level - 1]) {
+ /*
+ * Unless it is lan circuit participating in dis process
+ * the value is an empty octet string
+ */
+ *var_len = 0;
+ return circuit_id_val;
+ }
+
+ *var_len = ISIS_SYS_ID_LEN + 1;
+
+ if (level == IS_LEVEL_1)
+ return circuit->u.bc.l1_desig_is;
+
+ return circuit->u.bc.l2_desig_is;
+
+ case ISIS_CIRCLEVEL_HELLOMULTIPLIER:
+ return SNMP_INTEGER(circuit->hello_multiplier[level - 1]);
+
+ case ISIS_CIRCLEVEL_HELLOTIMER:
+ return SNMP_INTEGER(circuit->hello_interval[level - 1] * 1000);
+
+ case ISIS_CIRCLEVEL_DRHELLOTIMER:
+ return SNMP_INTEGER(circuit->hello_interval[level - 1] * 1000);
+
+ case ISIS_CIRCLEVEL_LSPTHROTTLE:
+ if (circuit->area)
+ return SNMP_INTEGER(
+ circuit->area->min_spf_interval[level - 1]
+ * 1000);
+ else
+ return SNMP_INTEGER(0);
+
+ case ISIS_CIRCLEVEL_MINLSPRETRANSINT:
+ if (circuit->area)
+ return SNMP_INTEGER(
+ circuit->area->min_spf_interval[level - 1]);
+ else
+ return SNMP_INTEGER(0);
+
+ case ISIS_CIRCLEVEL_CSNPINTERVAL:
+ return SNMP_INTEGER(circuit->csnp_interval[level - 1]);
+
+ case ISIS_CIRCLEVEL_PARTSNPINTERVAL:
+ return SNMP_INTEGER(circuit->psnp_interval[level - 1]);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_circ_counter(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index circuit-id 1-255 + level */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ struct isis_circuit *circuit;
+ int level;
+ uint32_t val = 0;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_circuit_level_lookup_exact(oid_idx, oid_idx_len,
+ 1, &circuit, &level);
+
+ if (!res || oid_idx_len != 2)
+ return NULL;
+
+ } else {
+ res = isis_snmp_circuit_level_lookup_next(oid_idx, oid_idx_len,
+ 1, &circuit, &level);
+
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = circuit->snmp_id;
+ if (circuit->circ_type == CIRCUIT_T_P2P)
+ name[v->namelen + 1] = ISIS_SNMP_P2P_CIRCUIT;
+ else
+ name[v->namelen + 1] = level;
+
+ /* Set length */
+ *length = v->namelen + 2;
+ }
+
+ switch (v->magic) {
+ case ISIS_CIRC_ADJCHANGES:
+ val = circuit->adj_state_changes;
+ break;
+
+ case ISIS_CIRC_NUMADJ:
+ if (circuit->circ_type == CIRCUIT_T_P2P) {
+ val = circuit->u.p2p.neighbor == NULL ? 0 : 1;
+ break;
+ }
+
+ if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
+ val = 0;
+ break;
+ }
+
+ if (level == IS_LEVEL_1) {
+ if (circuit->u.bc.adjdb[0] == NULL)
+ val = 0;
+ else
+ val = listcount(circuit->u.bc.adjdb[0]);
+ break;
+ }
+
+ if (circuit->u.bc.adjdb[1] == NULL)
+ val = 0;
+ else
+ val = listcount(circuit->u.bc.adjdb[1]);
+
+ break;
+
+ case ISIS_CIRC_INITFAILS:
+ val = circuit->init_failures; /* counter never incremented */
+ break;
+
+ case ISIS_CIRC_REJADJS:
+ val = circuit->rej_adjacencies;
+ break;
+
+ case ISIS_CIRC_IDFIELDLENMISMATCHES:
+ val = circuit->id_len_mismatches;
+ break;
+
+ case ISIS_CIRC_MAXAREAADDRMISMATCHES:
+ val = circuit->max_area_addr_mismatches;
+ break;
+
+ case ISIS_CIRC_AUTHTYPEFAILS:
+ val = circuit->auth_type_failures;
+ break;
+
+ case ISIS_CIRC_AUTHFAILS:
+ val = circuit->auth_failures;
+ break;
+
+ case ISIS_CIRC_LANDESISCHANGES:
+ if (circuit->circ_type == CIRCUIT_T_P2P)
+ val = 0;
+ else
+ val = circuit->desig_changes[level - 1];
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return SNMP_INTEGER(val);
+}
+
+static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index is circuit-id: 1-255 + adj-id: 1-... */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ uint32_t val;
+ struct isis_adjacency *adj;
+ uint32_t up_ticks;
+ uint32_t delta_ticks;
+ uint32_t now_time;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_NONE, &adj,
+ NULL, NULL, NULL);
+
+ if (!res || oid_idx_len != 2)
+ return NULL;
+
+ } else {
+ res = isis_snmp_adj_lookup_next(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_NONE, &adj,
+ NULL, NULL, NULL);
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = adj->circuit->snmp_id;
+ name[v->namelen + 1] = adj->snmp_idx;
+
+ /* Set length */
+ *length = v->namelen + 2;
+ }
+
+ switch (v->magic) {
+ case ISIS_ISADJ_STATE:
+ val = ISIS_SNMP_ADJ_STATE_DOWN;
+
+ switch (adj->adj_state) {
+ case ISIS_ADJ_UNKNOWN:
+ case ISIS_ADJ_DOWN:
+ val = ISIS_SNMP_ADJ_STATE_DOWN;
+ break;
+
+ case ISIS_ADJ_INITIALIZING:
+ val = ISIS_SNMP_ADJ_STATE_INITIALIZING;
+ break;
+
+ case ISIS_ADJ_UP:
+ val = ISIS_SNMP_ADJ_STATE_UP;
+ break;
+ }
+
+ return SNMP_INTEGER(val);
+
+ case ISIS_ISADJ_3WAYSTATE:
+ return SNMP_INTEGER(adj->threeway_state);
+
+ case ISIS_ISADJ_NEIGHSNPAADDRESS: {
+ const char *snpa = (char *)snpa_print(adj->snpa);
+ *var_len = strlen(snpa);
+ return (uint8_t *)snpa;
+ }
+
+ case ISIS_ISADJ_NEIGHSYSTYPE:
+ val = ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN;
+
+ switch (adj->sys_type) {
+ case ISIS_SYSTYPE_UNKNOWN:
+ case ISIS_SYSTYPE_ES:
+ val = ISIS_SNMP_ADJ_NEIGHTYPE_UNKNOWN;
+ break;
+
+ case ISIS_SYSTYPE_IS:
+ val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1_L2;
+ break;
+
+ case ISIS_SYSTYPE_L1_IS:
+ val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L1;
+ break;
+
+ case ISIS_SYSTYPE_L2_IS:
+ val = ISIS_SNMP_ADJ_NEIGHTYPE_IS_L2;
+ break;
+ }
+
+ return SNMP_INTEGER(val);
+
+ case ISIS_ISADJ_NEIGHSYSID:
+ *var_len = sizeof(adj->sysid);
+ return adj->sysid;
+
+ case ISIS_ISADJ_NBREXTENDEDCIRCID:
+ return SNMP_INTEGER(adj->ext_circuit_id != 0 ? 1 : 0);
+
+ case ISIS_ISADJ_USAGE:
+ /* It seems that no value conversion is required */
+ return SNMP_INTEGER(adj->adj_usage);
+
+ case ISIS_ISADJ_HOLDTIMER:
+ /*
+ * It seems that we want remaining timer
+ */
+ if (adj->last_upd != 0) {
+ val = isis_snmp_time();
+ if (val < (adj->last_upd + adj->hold_time))
+ return SNMP_INTEGER(adj->last_upd
+ + adj->hold_time - val);
+ }
+ /* Not running or just expired */
+ return SNMP_INTEGER(0);
+
+ case ISIS_ISADJ_NEIGHPRIORITY:
+ return SNMP_INTEGER(adj->prio[adj->level - 1]);
+
+ case ISIS_ISADJ_LASTUPTIME:
+ if (adj->flaps == 0)
+ return SNMP_INTEGER(0);
+
+ up_ticks = (uint32_t)netsnmp_get_agent_uptime();
+
+ now_time = isis_snmp_time();
+
+ if (adj->last_flap >= now_time)
+ return SNMP_INTEGER(up_ticks);
+
+ delta_ticks = (now_time - adj->last_flap) * 10;
+
+ if (up_ticks < delta_ticks)
+ return SNMP_INTEGER(up_ticks);
+
+ return SNMP_INTEGER(up_ticks - delta_ticks);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_isadj_area(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index circuit-id: 1-255 + adj-id: 1-... */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ struct isis_adjacency *adj;
+ oid data_idx;
+ uint8_t *data;
+ size_t data_len;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_AREA_ADDR,
+ &adj, NULL, &data, &data_len);
+
+ if (!res || oid_idx_len != 3)
+ return NULL;
+
+ } else {
+ res = isis_snmp_adj_lookup_next(
+ oid_idx, oid_idx_len, ISIS_SNMP_ADJ_DATA_AREA_ADDR,
+ &adj, &data_idx, &data, &data_len);
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = adj->circuit->snmp_id;
+ name[v->namelen + 1] = adj->snmp_idx;
+ name[v->namelen + 2] = data_idx;
+
+ /* Set length */
+ *length = v->namelen + 3;
+ }
+
+ switch (v->magic) {
+ case ISIS_ISADJAREA_ADDRESS:
+ *var_len = data_len;
+ return data;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_isadj_ipaddr(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index circuit-id 1-255 + adj-id 1-... */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ struct isis_adjacency *adj;
+ oid data_idx;
+ uint8_t *data;
+ size_t data_len;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_IP_ADDR,
+ &adj, NULL, &data, &data_len);
+
+ if (!res || oid_idx_len != 3)
+ return NULL;
+ } else {
+ res = isis_snmp_adj_lookup_next(
+ oid_idx, oid_idx_len, ISIS_SNMP_ADJ_DATA_IP_ADDR, &adj,
+ &data_idx, &data, &data_len);
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = adj->circuit->snmp_id;
+ name[v->namelen + 1] = adj->snmp_idx;
+ name[v->namelen + 2] = data_idx;
+
+ /* Set length */
+ *length = v->namelen + 3;
+ }
+
+ switch (v->magic) {
+ case ISIS_ISADJIPADDR_TYPE:
+ if (data_len == 4)
+ return SNMP_INTEGER(ISIS_SNMP_INET_TYPE_V4);
+
+ return SNMP_INTEGER(ISIS_SNMP_INET_TYPE_V6);
+
+ case ISIS_ISADJIPADDR_ADDRESS:
+ *var_len = data_len;
+ return data;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static uint8_t *isis_snmp_find_isadj_prot_supp(struct variable *v, oid *name,
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Index circuit-id 1-255 + adj-id 1-... */
+ oid *oid_idx;
+ size_t oid_idx_len;
+ int res;
+ struct isis_adjacency *adj;
+ oid data_idx;
+ uint8_t *data;
+ size_t data_len;
+
+ *write_method = NULL;
+
+ if (*length <= v->namelen) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else if (memcmp(name, v->name, v->namelen * sizeof(oid)) != 0) {
+ oid_idx = NULL;
+ oid_idx_len = 0;
+ } else {
+ oid_idx = name + v->namelen;
+ oid_idx_len = *length - v->namelen;
+ }
+ if (exact) {
+ res = isis_snmp_adj_lookup_exact(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_PROTO, &adj,
+ NULL, &data, &data_len);
+
+ if (!res || oid_idx_len != 3)
+ return NULL;
+
+ } else {
+ res = isis_snmp_adj_lookup_next(oid_idx, oid_idx_len,
+ ISIS_SNMP_ADJ_DATA_PROTO, &adj,
+ &data_idx, &data, &data_len);
+ if (!res)
+ return NULL;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ name[v->namelen] = adj->circuit->snmp_id;
+ name[v->namelen + 1] = adj->snmp_idx;
+ name[v->namelen + 2] = data_idx;
+
+ /* Set length */
+ *length = v->namelen + 3;
+ }
+
+ switch (v->magic) {
+ case ISIS_ISADJPROTSUPP_PROTOCOL:
+ return SNMP_INTEGER(*data);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+
+/* Register ISIS-MIB. */
+static int isis_snmp_init(struct thread_master *tm)
+{
+ struct isis_func_to_prefix *h2f = isis_func_to_prefix_arr;
+ struct variable *v;
+
+ for (size_t off = 0; off < isis_var_count; off++) {
+ v = &isis_var_arr[off];
+
+ if (v->findVar != h2f->ihtp_func) {
+ /* Next table */
+ h2f++;
+ assert(h2f < (isis_func_to_prefix_arr
+ + isis_func_to_prefix_count));
+ assert(v->findVar == h2f->ihtp_func);
+ }
+
+ v->namelen = h2f->ihtp_pref_len + 1;
+ memcpy(v->name, h2f->ihtp_pref_oid,
+ h2f->ihtp_pref_len * sizeof(oid));
+ v->name[h2f->ihtp_pref_len] = v->magic;
+ }
+
+
+ smux_init(tm);
+ REGISTER_MIB("mibII/isis", isis_var_arr, variable, isis_oid);
+ return 0;
+}
+
+/*
+ * ISIS notification functions: we have one function per notification
+ */
+static int isis_snmp_trap_throttle(oid trap_id)
+{
+ time_t time_now;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL || !isis->snmp_notifications || !smux_enabled())
+ return 0;
+
+ time_now = isis_snmp_time();
+
+ if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now)
+ /* Throttle trap rate at 1 in 5 secs */
+ return 0;
+
+ isis_snmp_trap_timestamp[trap_id] = time_now;
+ return 1;
+}
+
+static int isis_snmp_db_overload_update(const struct isis_area *area)
+{
+ netsnmp_variable_list *notification_vars;
+ long val;
+ uint32_t off;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_DB_OVERLOAD))
+ return 0;
+
+ notification_vars = NULL;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)&isis_snmp_trap_val_db_overload,
+ sizeof(isis_snmp_trap_val_db_overload));
+
+ /* Prepare data */
+ val = area->is_type;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+ /* Patch sys_level_state with proper index */
+ off = ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state) - 1;
+ isis_snmp_trap_data_var_sys_level_state[off] = val;
+
+ /* Prepare data */
+ if (area->overload_bit)
+ val = ISIS_SNMP_LEVEL_STATE_OVERLOADED;
+ else
+ val = ISIS_SNMP_LEVEL_STATE_ON;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_state,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+ return 0;
+}
+
+static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area,
+ const uint8_t *lsp_id)
+{
+ netsnmp_variable_list *notification_vars;
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_EXCEED_MAX))
+ return 0;
+
+ notification_vars = NULL;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)&isis_snmp_trap_val_lsp_exceed_max,
+ sizeof(isis_snmp_trap_val_lsp_exceed_max));
+
+ /* Prepare data */
+ val = area->is_type;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2);
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+ return 0;
+}
+
+
+/*
+ * A common function to handle popular combination of trap objects
+ * isisNotificationSysLevelIndex,
+ * optional-object-a
+ * isisNotificationCircIfIndex,
+ * optional-object-b
+ */
+static void isis_snmp_update_worker_a(const struct isis_circuit *circuit,
+ oid trap_id, const oid *oid_a,
+ size_t oid_a_len, uint8_t type_a,
+ const void *data_a, size_t data_a_len,
+ const oid *oid_b, size_t oid_b_len,
+ uint8_t type_b, const void *data_b,
+ size_t data_b_len)
+{
+ netsnmp_variable_list *notification_vars = NULL;
+ oid var_name[MAX_OID_LEN];
+ size_t var_count;
+ long val;
+
+ /* Sanity */
+ if (trap_id != ISIS_TRAP_ID_LEN_MISMATCH
+ && trap_id != ISIS_TRAP_MAX_AREA_ADDR_MISMATCH
+ && trap_id != ISIS_TRAP_OWN_LSP_PURGE
+ && trap_id != ISIS_TRAP_SEQNO_SKIPPED
+ && trap_id != ISIS_TRAP_AUTHEN_TYPE_FAILURE
+ && trap_id != ISIS_TRAP_AUTHEN_FAILURE
+ && trap_id != ISIS_TRAP_REJ_ADJACENCY)
+ return;
+
+ /* Put in trap value */
+ memcpy(var_name, isis_snmp_notifications,
+ sizeof(isis_snmp_notifications));
+ var_count = ARRAY_SIZE(isis_snmp_notifications);
+ var_name[var_count++] = trap_id;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)var_name, var_count * sizeof(oid));
+
+ val = circuit->is_type;
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+ if (oid_a_len != 0) {
+ if (oid_a == NULL || data_a == NULL || data_a_len == 0)
+ return;
+
+ snmp_varlist_add_variable(&notification_vars, oid_a, oid_a_len,
+ type_a, (uint8_t *)data_a,
+ data_a_len);
+ }
+
+ if (circuit->interface == NULL)
+ val = 0;
+ else
+ val = circuit->interface->ifindex;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_circ_if_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+
+ if (oid_b_len != 0) {
+ if (oid_b == NULL || data_b == NULL || data_b_len == 0)
+ return;
+
+ snmp_varlist_add_variable(&notification_vars, oid_b, oid_b_len,
+ type_b, (uint8_t *)data_b,
+ data_b_len);
+ }
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+}
+
+/*
+ * A common function to handle popular combination of trap objects
+ * isisNotificationSysLevelIndex,
+ * isisNotificationCircIfIndex,
+ * optional-var-a
+ * optional-var-b
+ *
+ * Note: the only difference with worker_a is order of circ-if-index vs
+ * optional-var-a
+ */
+static void isis_snmp_update_worker_b(const struct isis_circuit *circuit,
+ oid trap_id, const oid *oid_a,
+ size_t oid_a_len, uint8_t type_a,
+ const void *data_a, size_t data_a_len,
+ const oid *oid_b, size_t oid_b_len,
+ uint8_t type_b, const void *data_b,
+ size_t data_b_len)
+{
+ netsnmp_variable_list *notification_vars = NULL;
+ oid var_name[MAX_OID_LEN];
+ size_t var_count;
+ long val;
+
+ /* Sanity */
+ if (trap_id != ISIS_TRAP_VERSION_SKEW
+ && trap_id != ISIS_TRAP_LSP_TOO_LARGE
+ && trap_id != ISIS_TRAP_ADJ_STATE_CHANGE)
+ return;
+
+ /* Put in trap value */
+ memcpy(var_name, isis_snmp_notifications,
+ sizeof(isis_snmp_notifications));
+ var_count = ARRAY_SIZE(isis_snmp_notifications);
+ var_name[var_count++] = trap_id;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)var_name, var_count * sizeof(oid));
+
+ val = circuit->is_type;
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+ if (circuit->interface == NULL)
+ val = 0;
+ else
+ val = circuit->interface->ifindex;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_circ_if_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+
+ if (oid_a_len != 0) {
+ if (oid_a == NULL || data_a == NULL || data_a_len == 0)
+ return;
+
+ snmp_varlist_add_variable(&notification_vars, oid_a, oid_a_len,
+ type_a, (uint8_t *)data_a,
+ data_a_len);
+ }
+
+ if (oid_b_len != 0) {
+ if (oid_b == NULL || data_b == NULL || data_b_len == 0)
+ return;
+
+ snmp_varlist_add_variable(&notification_vars, oid_b, oid_b_len,
+ type_b, (uint8_t *)data_b,
+ data_b_len);
+ }
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+}
+
+
+static int isis_snmp_id_len_mismatch_update(const struct isis_circuit *circuit,
+ uint8_t rcv_id, const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_ID_LEN_MISMATCH))
+ return 0;
+
+ val = rcv_id;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_ID_LEN_MISMATCH,
+ isis_snmp_trap_data_var_pdu_field_len,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32,
+ &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int
+isis_snmp_max_area_addr_mismatch_update(const struct isis_circuit *circuit,
+ uint8_t max_addrs, const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_MAX_AREA_ADDR_MISMATCH))
+ return 0;
+
+ val = max_addrs;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_MAX_AREA_ADDR_MISMATCH,
+ isis_snmp_trap_data_var_pdu_max_area_addr,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_max_area_addr),
+ UNSIGNED32, &val, sizeof(val),
+ isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int isis_snmp_own_lsp_purge_update(const struct isis_circuit *circuit,
+ const uint8_t *lsp_id)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_OWN_LSP_PURGE))
+ return 0;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_OWN_LSP_PURGE, NULL, 0, STRING, NULL, 0,
+ isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2);
+ return 0;
+}
+
+static int isis_snmp_seqno_skipped_update(const struct isis_circuit *circuit,
+ const uint8_t *lsp_id)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_SEQNO_SKIPPED))
+ return 0;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_SEQNO_SKIPPED, NULL, 0, STRING, NULL, 0,
+ isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2);
+ return 0;
+}
+
+static int
+isis_snmp_authentication_type_failure_update(const struct isis_circuit *circuit,
+ const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_AUTHEN_TYPE_FAILURE))
+ return 0;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_AUTHEN_TYPE_FAILURE, NULL, 0, STRING, NULL,
+ 0, isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int
+isis_snmp_authentication_failure_update(const struct isis_circuit *circuit,
+ char const *raw_pdu, size_t raw_pdu_len)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_AUTHEN_FAILURE))
+ return 0;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_AUTHEN_FAILURE, NULL, 0, STRING, NULL, 0,
+ isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int isis_snmp_version_skew_update(const struct isis_circuit *circuit,
+ uint8_t version, const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_VERSION_SKEW))
+ return 0;
+
+ val = version;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_b(
+ circuit, ISIS_TRAP_VERSION_SKEW,
+ isis_snmp_trap_data_var_pdu_proto_ver,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32,
+ &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit,
+ const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ /*
+ * This is a special case because
+ * it does not include isisNotificationSysLevelIndex
+ */
+ netsnmp_variable_list *notification_vars;
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_AREA_MISMATCH))
+ return 0;
+
+ notification_vars = NULL;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)&isis_snmp_trap_val_area_mismatch,
+ sizeof(isis_snmp_trap_val_area_mismatch));
+
+
+ if (circuit->interface == NULL)
+ val = 0;
+ else
+ val = circuit->interface->ifindex;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_circ_if_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+
+ return 0;
+}
+
+static int isis_snmp_reject_adjacency_update(const struct isis_circuit *circuit,
+ const char *raw_pdu,
+ size_t raw_pdu_len)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_REJ_ADJACENCY))
+ return 0;
+
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ isis_snmp_update_worker_a(
+ circuit, ISIS_TRAP_REJ_ADJACENCY, NULL, 0, STRING, NULL, 0,
+ isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+ return 0;
+}
+
+static int isis_snmp_lsp_too_large_update(const struct isis_circuit *circuit,
+ uint32_t pdu_size,
+ const uint8_t *lsp_id)
+{
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_TOO_LARGE))
+ return 0;
+
+ isis_snmp_update_worker_b(
+ circuit, ISIS_TRAP_LSP_TOO_LARGE,
+ isis_snmp_trap_data_var_pdu_lsp_size,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32,
+ &pdu_size, sizeof(pdu_size), isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2);
+ return 0;
+}
+
+
+static int isis_snmp_adj_state_change_update(const struct isis_adjacency *adj)
+{
+ uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
+ long val;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL || !isis->snmp_notifications || !smux_enabled())
+ return 0;
+
+ /* Prepare data */
+ memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
+ lsp_id[ISIS_SYS_ID_LEN] = 0;
+ lsp_id[ISIS_SYS_ID_LEN + 1] = 0;
+
+ val = ISIS_SNMP_ADJ_STATE_DOWN;
+
+ switch (adj->adj_state) {
+ case ISIS_ADJ_UNKNOWN:
+ val = ISIS_SNMP_ADJ_STATE_DOWN;
+ break;
+
+ case ISIS_ADJ_INITIALIZING:
+ val = ISIS_SNMP_ADJ_STATE_INITIALIZING;
+ break;
+
+ case ISIS_ADJ_UP:
+ val = ISIS_SNMP_ADJ_STATE_UP;
+ break;
+
+ case ISIS_ADJ_DOWN:
+ val = ISIS_SNMP_ADJ_STATE_FAILED;
+ break;
+ }
+
+ isis_snmp_update_worker_b(
+ adj->circuit, ISIS_TRAP_ADJ_STATE_CHANGE,
+ isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2, isis_snmp_trap_data_var_adj_state,
+ ARRAY_SIZE(isis_snmp_trap_data_var_adj_state), INTEGER, &val,
+ sizeof(val));
+ return 0;
+}
+
+static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
+ const uint8_t *lsp_id,
+ char const *raw_pdu, size_t raw_pdu_len)
+{
+ /*
+ * This is a special case because
+ * it have more variables
+ */
+ netsnmp_variable_list *notification_vars;
+ long val;
+
+ if (!isis_snmp_trap_throttle(ISIS_TRAP_LSP_ERROR))
+ return 0;
+
+ notification_vars = NULL;
+
+ /* Put in trap value */
+ snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
+ ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+ (uint8_t *)&isis_snmp_trap_val_lsp_error,
+ sizeof(isis_snmp_trap_val_lsp_error));
+
+ /* Prepare data */
+ val = circuit->is_type;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_sys_level_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+ (uint8_t *)&val, sizeof(val));
+
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+ ISIS_SYS_ID_LEN + 2);
+
+ /* Prepare data */
+ if (circuit->interface == NULL)
+ val = 0;
+ else
+ val = circuit->interface->ifindex;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_circ_if_index,
+ ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+ /* Prepare data */
+ if (raw_pdu_len > ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN)
+ raw_pdu_len = ISIS_SNMP_TRAP_PDU_FRAGMENT_MAX_LEN;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_pdu_fragment,
+ ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+ raw_pdu, raw_pdu_len);
+
+ /* Prepare data */
+ val = 0;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_error_offset,
+ ARRAY_SIZE(isis_snmp_trap_data_var_error_offset), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+ /* Prepare data */
+ val = 0;
+
+ snmp_varlist_add_variable(
+ &notification_vars, isis_snmp_trap_data_var_error_tlv_type,
+ ARRAY_SIZE(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32,
+ (uint8_t *)&val, sizeof(val));
+
+ send_v2trap(notification_vars);
+ snmp_free_varbind(notification_vars);
+ smux_events_update();
+ return 0;
+}
+
+
+static int isis_snmp_module_init(void)
+{
+ hook_register(isis_hook_db_overload, isis_snmp_db_overload_update);
+ hook_register(isis_hook_lsp_exceed_max,
+ isis_snmp_lsp_exceed_max_update);
+ hook_register(isis_hook_id_len_mismatch,
+ isis_snmp_id_len_mismatch_update);
+ hook_register(isis_hook_max_area_addr_mismatch,
+ isis_snmp_max_area_addr_mismatch_update);
+ hook_register(isis_hook_own_lsp_purge, isis_snmp_own_lsp_purge_update);
+ hook_register(isis_hook_seqno_skipped, isis_snmp_seqno_skipped_update);
+ hook_register(isis_hook_authentication_type_failure,
+ isis_snmp_authentication_type_failure_update);
+ hook_register(isis_hook_authentication_failure,
+ isis_snmp_authentication_failure_update);
+ hook_register(isis_hook_version_skew, isis_snmp_version_skew_update);
+ hook_register(isis_hook_area_mismatch, isis_snmp_area_mismatch_update);
+ hook_register(isis_hook_reject_adjacency,
+ isis_snmp_reject_adjacency_update);
+ hook_register(isis_hook_lsp_too_large, isis_snmp_lsp_too_large_update);
+ hook_register(isis_hook_adj_state_change,
+ isis_snmp_adj_state_change_update);
+ hook_register(isis_hook_lsp_error, isis_snmp_lsp_error_update);
+
+ hook_register(frr_late_init, isis_snmp_init);
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "isis_snmp", .version = FRR_VERSION,
+ .description = "isis AgentX SNMP module",
+ .init = isis_snmp_module_init, )
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 22dfee994f..7bcc6fea90 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -1821,6 +1821,7 @@ static int isis_run_spf_cb(struct thread *thread)
struct isis_spf_run *run = THREAD_ARG(thread);
struct isis_area *area = run->area;
int level = run->level;
+ int have_run = 0;
XFREE(MTYPE_ISIS_SPF_RUN, run);
area->spf_timer[level - 1] = NULL;
@@ -1839,15 +1840,24 @@ static int isis_run_spf_cb(struct thread *thread)
zlog_debug("ISIS-SPF (%s) L%d SPF needed, periodic SPF",
area->area_tag, level);
- if (area->ip_circuits)
+ if (area->ip_circuits) {
isis_run_spf_with_protection(
area, area->spftree[SPFTREE_IPV4][level - 1]);
- if (area->ipv6_circuits)
+ have_run = 1;
+ }
+ if (area->ipv6_circuits) {
isis_run_spf_with_protection(
area, area->spftree[SPFTREE_IPV6][level - 1]);
- if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area))
+ have_run = 1;
+ }
+ if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) {
isis_run_spf_with_protection(
area, area->spftree[SPFTREE_DSTSRC][level - 1]);
+ have_run = 1;
+ }
+
+ if (have_run)
+ area->spf_run_count[level]++;
isis_area_verify_routes(area);
diff --git a/isisd/isisd.c b/isisd/isisd.c
index a802bac13b..487a902c06 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -89,6 +89,10 @@ static struct isis_master isis_master;
/* ISIS process wide configuration pointer to export. */
struct isis_master *im;
+#ifndef FABRICD
+DEFINE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area));
+#endif /* ifndef FABRICD */
+
/*
* Prototypes.
*/
@@ -214,6 +218,7 @@ struct isis *isis_new(const char *vrf_name)
isis->area_list = list_new();
isis->init_circ_list = list_new();
isis->uptime = time(NULL);
+ isis->snmp_notifications = 1;
dyn_cache_init(isis);
return isis;
@@ -2563,6 +2568,14 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit)
if (new_overload_bit != area->overload_bit) {
area->overload_bit = new_overload_bit;
+
+ if (new_overload_bit)
+ area->overload_counter++;
+
+#ifndef FABRICD
+ hook_call(isis_hook_db_overload, area);
+#endif /* ifndef FABRICD */
+
lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
}
#ifndef FABRICD
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 22d9c6236d..1b0ec2b4f0 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -63,6 +63,8 @@ extern void isis_cli_init(void);
all_vrf = strmatch(vrf_name, "all"); \
}
+#define SNMP_CIRCUITS_MAX (512)
+
extern struct zebra_privs_t isisd_privs;
/* uncomment if you are a developer in bug hunt */
@@ -93,6 +95,9 @@ struct isis {
time_t uptime; /* when did we start */
struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */
uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */
+ struct isis_circuit *snmp_circuits[SNMP_CIRCUITS_MAX];
+ uint32_t snmp_circuit_id_last;
+ int snmp_notifications;
struct route_table *ext_info[REDIST_PROTOCOL_COUNT];
struct ldp_sync_info_cmd ldp_sync_cmd; /* MPLS LDP-IGP Sync */
@@ -168,6 +173,7 @@ struct isis_area {
char is_type; /* level-1 level-1-2 or level-2-only */
/* are we overloaded? */
char overload_bit;
+ uint32_t overload_counter;
/* L1/L2 router identifier for inter-area traffic */
char attached_bit_send;
char attached_bit_rcv_ignore;
@@ -180,6 +186,9 @@ struct isis_area {
int lsp_frag_threshold;
uint64_t lsp_gen_count[ISIS_LEVELS];
uint64_t lsp_purge_count[ISIS_LEVELS];
+ uint32_t lsp_exceeded_max_counter;
+ uint32_t lsp_seqno_skipped_counter;
+ uint64_t spf_run_count[ISIS_LEVELS];
int ip_circuits;
/* logging adjacency changes? */
uint8_t log_adj_changes;
@@ -220,10 +229,19 @@ struct isis_area {
pdu_counter_t pdu_rx_counters;
uint64_t lsp_rxmt_count;
+ /* Area counters */
+ uint64_t rej_adjacencies[2];
+ uint64_t auth_type_failures[2];
+ uint64_t auth_failures[2];
+ uint64_t id_len_mismatches[2];
+ uint64_t lsp_error_counter[2];
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(isis_area)
+DECLARE_HOOK(isis_area_overload_bit_update, (struct isis_area * area), (area))
+
void isis_terminate(void);
void isis_finish(struct isis *isis);
void isis_master_init(struct thread_master *master);
diff --git a/isisd/subdir.am b/isisd/subdir.am
index 4be4efc118..98674a6881 100644
--- a/isisd/subdir.am
+++ b/isisd/subdir.am
@@ -17,6 +17,9 @@ vtysh_scan += \
isisd/isisd.c \
# end
vtysh_daemons += isisd
+if SNMP
+module_LTLIBRARIES += isisd/isisd_snmp.la
+endif
man8 += $(MANBUILD)/frr-isisd.8
endif
@@ -137,7 +140,12 @@ isisd_isisd_SOURCES = $(ISIS_SOURCES)
nodist_isisd_isisd_SOURCES = \
yang/frr-isisd.yang.c \
# end
-
+
+isisd_isisd_snmp_la_SOURCES = isisd/isis_snmp.c
+isisd_isisd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
+isisd_isisd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+isisd_isisd_snmp_la_LIBADD = lib/libfrrsnmp.la
+
# Building fabricd
FABRICD_CPPFLAGS = -DFABRICD=1 $(AM_CPPFLAGS)
diff --git a/ldpd/lde.c b/ldpd/lde.c
index 69338b8bad..8fa74d1c3d 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -127,15 +127,13 @@ static struct quagga_signal_t lde_signals[] =
void
lde(void)
{
- struct thread thread;
-
#ifdef HAVE_SETPROCTITLE
setproctitle("label decision engine");
#endif
ldpd_process = PROC_LDE_ENGINE;
log_procname = log_procnames[PROC_LDE_ENGINE];
- master = thread_master_create(NULL);
+ master = frr_init();
/* setup signal handler */
signal_init(master, array_size(lde_signals), lde_signals);
@@ -157,9 +155,12 @@ lde(void)
/* create base configuration */
ldeconf = config_new_empty();
- /* Fetch next active thread. */
+ struct thread thread;
while (thread_fetch(master, &thread))
thread_call(&thread);
+
+ /* NOTREACHED */
+ return;
}
void
@@ -566,6 +567,9 @@ lde_dispatch_parent(struct thread *thread)
memcpy(&init, imsg.data, sizeof(init));
lde_init(&init);
break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
case IMSG_RECONF_CONF:
if ((nconf = malloc(sizeof(struct ldpd_conf))) ==
NULL)
diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c
new file mode 100644
index 0000000000..97dde616a7
--- /dev/null
+++ b/ldpd/ldp_snmp.c
@@ -0,0 +1,1244 @@
+/*
+ * LDP SNMP support
+ * Copyright (C) 2020 Volta Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This is minimal read-only implementations providing
+ * mplsLdpModuleReadOnlyCompliance as described in RFC 3815.
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "vrf.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "memory.h"
+#include "smux.h"
+#include "libfrr.h"
+#include "version.h"
+#include "ldpd.h"
+#include "ldpe.h"
+
+/* SNMP value hack. */
+#define COUNTER32 ASN_COUNTER
+#define INTEGER ASN_INTEGER
+#define UNSIGNED32 ASN_GAUGE
+#define TIMESTAMP ASN_TIMETICKS
+#define TIMETICKS ASN_TIMETICKS
+#define STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+
+#define LDP_LSRID_IDX_LEN 6
+#define LDP_ENTITY_IDX_LEN 1
+#define LDP_ADJACENCY_IDX_LEN 1
+
+/* MPLS-LDP-STD-MIB. */
+#define MPLS_LDP_STD_MIB 1, 3, 6, 1, 2, 1, 10, 166, 4
+
+#define MPLS_LDP_LSR_ID 0
+#define MPLS_LDP_LSR_LOOP_DETECTION_CAPABLE 0
+#define MPLS_LDP_ENTITY_LAST_CHANGE 0
+#define MPLS_LDP_ENTITY_INDEX_NEXT 0
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* LDP-MIB instances. */
+static oid ldp_oid[] = {MPLS_LDP_STD_MIB};
+static oid ldp_trap_oid[] = {MPLS_LDP_STD_MIB, 0};
+
+static uint8_t snmp_ldp_rtrid[6] = {0, 0, 0, 0, 0};
+
+#define LDP_DEFAULT_ENTITY_INDEX 1
+
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_NONE 1
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_OTHER 2
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_HOPCOUNT 3
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_PATHVECTOR 4
+#define MPLSLDPLSRLOOPDETECTIONCAPABLE_HOPCOUNTANDPATHVECTOR 5
+
+/* MPLS LDP mplsLdpHelloAdjacencyTable. */
+#define MPLSLDPHELLOADJACENCYINDEX 1
+#define MPLSLDPHELLOADJACENCYHOLDTIMEREM 2
+#define MPLSLDPHELLOADJACENCYHOLDTIME 3
+#define MPLSLDPHELLOADJACENCYTYPE 4
+
+/* enums for column mplsLdpHelloAdjacencyType */
+#define MPLSLDPHELLOADJACENCYTYPE_LINK 1
+#define MPLSLDPHELLOADJACENCYTYPE_TARGETED 2
+
+#define MPLSLDPPEERTRANSPORTADDRTYPE_UNKNOWN 0
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV4 1
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV6 2
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV4Z 3
+#define MPLSLDPPEERTRANSPORTADDRTYPE_IPV6Z 4
+#define MPLSLDPPEERTRANSPORTADDRTYPE_DNS 16
+
+#define DOWNSTREAMONDEMAND 1
+#define DOWNSTREAMUNSOLICITED 2
+
+#define CONSERVATIVERETENTION 1
+#define LIBERALRETENTION 2
+
+#define TRANSPORTADDRINTERFACE 1
+#define TRANSPORTADDRLOOPBACK 2
+
+#define LABELTYPEGENERIC 1
+
+#define STORAGETYPENONVOLATILE 3
+
+#define ROWSTATUSACTIVE 4
+
+#define ADMINSTATUSENABLED 1
+
+#define OPERSTATUSENABLED 2
+
+/* MPLS LDP mplsLdpPeerTable */
+#define MPLSLDPPEERLDPID 1
+#define MPLSLDPPEERLABELDISTMETHOD 2
+#define MPLSLDPPEERPATHVECTORLIMIT 3
+#define MPLSLDPPEERTRANSPORTADDRTYPE 4
+#define MPLSLDPPEERTRANSPORTADDR 5
+
+#define MPLSLDPSESSIONROLE_UNKNOWN 1
+#define MPLSLDPSESSIONROLE_ACTIVE 2
+#define MPLSLDPSESSIONROLE_PASSIVE 3
+
+#define MPLSLDPSESSIONSTATE_NONEXISTENT 1
+#define MPLSLDPSESSIONSTATE_INITIALIZED 2
+#define MPLSLDPSESSIONSTATE_OPENREC 3
+#define MPLSLDPSESSIONSTATE_OPENSENT 4
+#define MPLSLDPSESSIONSTATE_OPERATIONAL 5
+
+/* MPLS LDP mplsLdpSessionTable */
+#define MPLSLDPSESSIONSTATELASTCHANGE 1
+#define MPLSLDPSESSIONSTATE 2
+#define MPLSLDPSESSIONROLE 3
+#define MPLSLDPSESSIONPROTOCOLVERSION 4
+#define MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM 5
+#define MPLSLDPSESSIONKEEPALIVETIME 6
+#define MPLSLDPSESSIONMAXPDULENGTH 7
+#define MPLSLDPSESSIONDISCONTINUITYTIME 8
+
+/* MPLS LDP mplsLdpEntityTable */
+#define MPLSLDPENTITYLDPID 1
+#define MPLSLDPENTITYINDEX 2
+#define MPLSLDPENTITYPROTOCOLVERSION 3
+#define MPLSLDPENTITYADMINSTATUS 4
+#define MPLSLDPENTITYOPERSTATUS 5
+#define MPLSLDPENTITYTCPPORT 6
+#define MPLSLDPENTITYUDPDSCPORT 7
+#define MPLSLDPENTITYMAXPDULENGTH 8
+#define MPLSLDPENTITYKEEPALIVEHOLDTIMER 9
+#define MPLSLDPENTITYHELLOHOLDTIMER 10
+#define MPLSLDPENTITYINITSESSIONTHRESHOLD 11
+#define MPLSLDPENTITYLABELDISTMETHOD 12
+#define MPLSLDPENTITYLABELRETENTIONMODE 13
+#define MPLSLDPENTITYPATHVECTORLIMIT 14
+#define MPLSLDPENTITYHOPCOUNTLIMIT 15
+#define MPLSLDPENTITYTRANSPORTADDRKIND 16
+#define MPLSLDPENTITYTARGETPEER 17
+#define MPLSLDPENTITYTARGETPEERADDRTYPE 18
+#define MPLSLDPENTITYTARGETPEERADDR 19
+#define MPLSLDPENTITYLABELTYPE 20
+#define MPLSLDPENTITYDISCONTINUITYTIME 21
+#define MPLSLDPENTITYSTORAGETYPE 22
+#define MPLSLDPENTITYROWSTATUS 23
+
+/* MPLS LDP mplsLdpEntityStatsTable */
+#define MPLSLDPENTITYSTATSSESSIONATTEMPTS 1
+#define MPLSLDPENTITYSTATSSESSIONREJHELLO 2
+#define MPLSLDPENTITYSTATSSESSIONREJAD 3
+#define MPLSLDPENTITYSTATSSESSIONREJMAXPDU 4
+#define MPLSLDPENTITYSTATSSESSIONREJLR 5
+#define MPLSLDPENTITYSTATSBADLDPID 6
+#define MPLSLDPENTITYSTATSBADPDULENGTH 7
+#define MPLSLDPENTITYSTATSBADMSGLENGTH 8
+#define MPLSLDPENTITYSTATSBADTLVLENGTH 9
+#define MPLSLDPENTITYSTATSMALFORMEDTLV 10
+#define MPLSLDPENTITYSTATSKEEPALIVEEXP 11
+#define MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY 12
+#define MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY 13
+
+#define MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS 1
+#define MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS 2
+
+static uint8_t *ldpLsrId(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ *var_len = 4;
+ return (uint8_t *)&leconf->rtr_id.s_addr;
+}
+
+static uint8_t *ldpLoopDetectCap(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(MPLSLDPLSRLOOPDETECTIONCAPABLE_NONE);
+}
+
+extern uint32_t ldp_start_time;
+static uint8_t *ldpEntityLastChange(struct variable *v, oid name[],
+ size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ *var_len = sizeof(time_t);
+ return (uint8_t *) &(leconf->config_change_time);
+
+}
+
+static uint8_t *ldpEntityIndexNext(struct variable *v, oid name[],
+ size_t *length,int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(0);
+}
+
+#define LDP_ENTITY_TOTAL_LEN 21
+#define LDP_ENTITY_MAX_IDX_LEN 6
+
+static struct ldpd_af_conf *ldpEntityTable_lookup(struct variable *v, oid *name,
+ size_t *length, int exact,
+ uint32_t *index)
+{
+ int len;
+ struct ldpd_af_conf *af_v4, *af_v6;
+
+ af_v4 = &leconf->ipv4;
+ af_v6 = &leconf->ipv6;
+
+ if (exact) {
+ if (*length != LDP_ENTITY_TOTAL_LEN)
+ return NULL;
+
+ if (leconf->trans_pref == DUAL_STACK_LDPOV6 &&
+ af_v6->flags & F_LDPD_AF_ENABLED) {
+ *index = 2;
+ return af_v6;
+ } else {
+ *index = 1;
+ return af_v4;
+ }
+ } else {
+ /* only support one router id so can just skip */
+ len = *length - v->namelen - LDP_ENTITY_MAX_IDX_LEN;
+ if (len <= 0) {
+ if (leconf->trans_pref == DUAL_STACK_LDPOV6 &&
+ af_v6->flags & F_LDPD_AF_ENABLED) {
+ *index = 2;
+ return af_v6;
+ } else {
+ *index = 1;
+ return af_v4;
+ }
+ }
+ }
+ return NULL;
+}
+
+static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct ldpd_af_conf *af;
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t index = 0;
+
+ *write_method = NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ af = ldpEntityTable_lookup(v, name, length, exact, &index);
+ if (af == NULL)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ *length = LDP_ENTITY_TOTAL_LEN;
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ IN_ADDR_SIZE);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ }
+
+ /* Return the current value of the variable */
+ switch (v->magic) {
+ case MPLSLDPENTITYLDPID:
+ *var_len = 6;
+ memcpy (snmp_ldp_rtrid, &entityLdpId, IN_ADDR_SIZE);
+ return (uint8_t *)snmp_ldp_rtrid;
+ case MPLSLDPENTITYINDEX:
+ return SNMP_INTEGER(LDP_DEFAULT_ENTITY_INDEX);
+ case MPLSLDPENTITYPROTOCOLVERSION:
+ return SNMP_INTEGER(LDP_VERSION);
+ case MPLSLDPENTITYADMINSTATUS:
+ return SNMP_INTEGER(ADMINSTATUSENABLED);
+ case MPLSLDPENTITYOPERSTATUS:
+ return SNMP_INTEGER(OPERSTATUSENABLED);
+ case MPLSLDPENTITYTCPPORT:
+ return SNMP_INTEGER(LDP_PORT);
+ case MPLSLDPENTITYUDPDSCPORT:
+ return SNMP_INTEGER(LDP_PORT);
+ case MPLSLDPENTITYMAXPDULENGTH:
+ return SNMP_INTEGER(LDP_MAX_LEN);
+ case MPLSLDPENTITYKEEPALIVEHOLDTIMER:
+ return SNMP_INTEGER(af->keepalive);
+ case MPLSLDPENTITYHELLOHOLDTIMER:
+ return SNMP_INTEGER(af->lhello_holdtime);
+ case MPLSLDPENTITYINITSESSIONTHRESHOLD:
+ return SNMP_INTEGER(0); /* not supported */
+ case MPLSLDPENTITYLABELDISTMETHOD:
+ return SNMP_INTEGER(DOWNSTREAMUNSOLICITED);
+ case MPLSLDPENTITYLABELRETENTIONMODE:
+ return SNMP_INTEGER(LIBERALRETENTION);
+ case MPLSLDPENTITYPATHVECTORLIMIT:
+ return SNMP_INTEGER(0); /* not supported */
+ case MPLSLDPENTITYHOPCOUNTLIMIT:
+ return SNMP_INTEGER(0);
+ case MPLSLDPENTITYTRANSPORTADDRKIND:
+ return SNMP_INTEGER(TRANSPORTADDRLOOPBACK);
+ case MPLSLDPENTITYTARGETPEER:
+ return SNMP_INTEGER(1);
+ case MPLSLDPENTITYTARGETPEERADDRTYPE:
+ if (index == 1)
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV4);
+ else
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV6);
+ case MPLSLDPENTITYTARGETPEERADDR:
+ if (index == 1) {
+ *var_len = sizeof(af->trans_addr.v4);
+ return ((uint8_t *)&af->trans_addr.v4);
+ }else {
+ *var_len = sizeof(af->trans_addr.v6);
+ return ((uint8_t *)&af->trans_addr.v6);
+ }
+ case MPLSLDPENTITYLABELTYPE:
+ return SNMP_INTEGER(LABELTYPEGENERIC);
+ case MPLSLDPENTITYDISCONTINUITYTIME:
+ return SNMP_INTEGER(0);
+ case MPLSLDPENTITYSTORAGETYPE:
+ return SNMP_INTEGER(STORAGETYPENONVOLATILE);
+ case MPLSLDPENTITYROWSTATUS:
+ return SNMP_INTEGER(ROWSTATUSACTIVE);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ int len;
+
+ *write_method = NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ if (exact) {
+ if (*length != LDP_ENTITY_TOTAL_LEN)
+ return NULL;
+ } else {
+ len = *length - v->namelen - LDP_ENTITY_MAX_IDX_LEN;
+ if (len > 0)
+ return NULL;
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ *length = LDP_ENTITY_TOTAL_LEN;
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ IN_ADDR_SIZE);
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ }
+
+ /* Return the current value of the variable */
+ switch (v->magic) {
+ case MPLSLDPENTITYSTATSSESSIONATTEMPTS:
+ return SNMP_INTEGER(leconf->stats.session_attempts);
+ case MPLSLDPENTITYSTATSSESSIONREJHELLO:
+ return SNMP_INTEGER(leconf->stats.session_rejects_hello);
+ case MPLSLDPENTITYSTATSSESSIONREJAD:
+ return SNMP_INTEGER(leconf->stats.session_rejects_ad);
+ case MPLSLDPENTITYSTATSSESSIONREJMAXPDU:
+ return SNMP_INTEGER(leconf->stats.session_rejects_max_pdu);
+ case MPLSLDPENTITYSTATSSESSIONREJLR:
+ return SNMP_INTEGER(leconf->stats.session_rejects_lr);
+ case MPLSLDPENTITYSTATSBADLDPID:
+ return SNMP_INTEGER(leconf->stats.bad_ldp_id);
+ case MPLSLDPENTITYSTATSBADPDULENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_pdu_len);
+ case MPLSLDPENTITYSTATSBADMSGLENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_msg_len);
+ case MPLSLDPENTITYSTATSBADTLVLENGTH:
+ return SNMP_INTEGER(leconf->stats.bad_tlv_len);
+ case MPLSLDPENTITYSTATSMALFORMEDTLV:
+ return SNMP_INTEGER(leconf->stats.malformed_tlv);
+ case MPLSLDPENTITYSTATSKEEPALIVEEXP:
+ return SNMP_INTEGER(leconf->stats.keepalive_timer_exp);
+ case MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY:
+ return SNMP_INTEGER(leconf->stats.shutdown_rcv_notify);
+ case MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY:
+ return SNMP_INTEGER(leconf->stats.shutdown_send_notify);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#define LDP_ADJACENCY_ENTRY_MAX_IDX_LEN 14
+
+static void ldpHelloAdjacencyTable_oid_to_index(
+ struct variable *v, oid name[],
+ size_t *length,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ oid *offset = name + v->namelen;
+ int offsetlen = *length - v->namelen;
+ int len = offsetlen;
+
+ if (len > LDP_ADJACENCY_ENTRY_MAX_IDX_LEN)
+ len = LDP_ADJACENCY_ENTRY_MAX_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), entityLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ENTITY_IDX_LEN)
+ len = LDP_ENTITY_IDX_LEN;
+
+ if (len >= LDP_ENTITY_IDX_LEN)
+ *entityIndex = offset[0];
+
+ offset += LDP_ENTITY_IDX_LEN;
+ offsetlen -= LDP_ENTITY_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_LSRID_IDX_LEN)
+ len = LDP_LSRID_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), peerLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ADJACENCY_IDX_LEN)
+ len = LDP_ADJACENCY_IDX_LEN;
+
+ if (len >= LDP_ADJACENCY_IDX_LEN)
+ *adjacencyIndex = offset[0];
+}
+
+static struct adj *
+nbr_get_adj_by_index(struct nbr *nbr, uint32_t adjacencyIndex)
+{
+ struct adj *adj;
+ uint32_t i = 0;
+
+ RB_FOREACH(adj, nbr_adj_head, &nbr->adj_tree)
+ if (++i == adjacencyIndex)
+ return adj;
+
+ return NULL;
+}
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_lookup_helper(
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *ctl_adj = NULL;
+ struct adj *adj = NULL;
+ struct nbr *cur_nbr = nbr_find_ldpid(peerLdpId->s_addr);
+
+ if (cur_nbr)
+ /* If found nbr, then look to see if the
+ * adjacency exists
+ */
+ adj = nbr_get_adj_by_index(cur_nbr, *adjacencyIndex);
+
+ if (adj)
+ ctl_adj = adj_to_ctl(adj);
+
+ return ctl_adj;
+}
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_next_helper(
+ int first,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *ctl_adj = NULL;
+ struct nbr *nbr = NULL;
+ struct adj *adj = NULL;
+
+ if (first)
+ nbr = nbr_get_first_ldpid();
+ else {
+ struct nbr *cur_nbr = nbr_find_ldpid(peerLdpId->s_addr);
+ if (cur_nbr)
+ /* If found nbr, then look to see if the
+ * adjacency exists
+ */
+ adj = nbr_get_adj_by_index(cur_nbr, *adjacencyIndex + 1);
+ if (adj)
+ *adjacencyIndex += 1;
+ else
+ nbr = nbr_get_next_ldpid(peerLdpId->s_addr);
+ }
+
+ if (!adj && nbr) {
+ adj = RB_MIN(nbr_adj_head, &nbr->adj_tree);
+ *adjacencyIndex = 1;
+ }
+
+ if (adj)
+ ctl_adj = adj_to_ctl(adj);
+
+ return ctl_adj;
+}
+
+#define HELLO_ADJ_MAX_IDX_LEN 14
+
+static struct ctl_adj *
+ldpHelloAdjacencyTable_lookup(struct variable *v, oid name[],
+ size_t *length, int exact,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId,
+ uint32_t *adjacencyIndex)
+{
+ struct ctl_adj *hello_adj = NULL;
+
+ if (exact) {
+ if (*length < HELLO_ADJ_MAX_IDX_LEN)
+ return NULL;
+
+ ldpHelloAdjacencyTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ hello_adj = ldpHelloAdjacencyTable_lookup_helper(
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+ } else {
+ int first = 0;
+ int offsetlen = *length - v->namelen;
+
+ if (offsetlen < HELLO_ADJ_MAX_IDX_LEN)
+ first = 1;
+
+ ldpHelloAdjacencyTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ hello_adj = ldpHelloAdjacencyTable_next_helper(first,
+ entityLdpId, entityIndex, peerLdpId, adjacencyIndex);
+
+ }
+ return hello_adj;
+}
+
+static uint8_t *ldpHelloAdjacencyTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ uint32_t adjacencyIndex = 0;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ struct ctl_adj *ctl_adj = ldpHelloAdjacencyTable_lookup(v, name,
+ length, exact,
+ &entityLdpId, &entityIndex, &peerLdpId, &adjacencyIndex);
+
+ if (!ctl_adj)
+ return NULL;
+
+ if (!exact) {
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ struct in_addr entityLdpId = {.s_addr = 0};
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+
+ struct in_addr peerLdpId = ctl_adj->id;
+
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
+ oid_copy_addr(name + v->namelen + 7, &peerLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+ name[v->namelen + 13] = adjacencyIndex;
+
+ /* Set length */
+ *length = v->namelen + HELLO_ADJ_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPHELLOADJACENCYINDEX:
+ return SNMP_INTEGER(adjacencyIndex);
+ case MPLSLDPHELLOADJACENCYHOLDTIMEREM:
+ return SNMP_INTEGER(ctl_adj->holdtime_remaining);
+ case MPLSLDPHELLOADJACENCYHOLDTIME:
+ return SNMP_INTEGER(ctl_adj->holdtime);
+ case MPLSLDPHELLOADJACENCYTYPE:
+ if (ctl_adj->type == HELLO_LINK)
+ return SNMP_INTEGER(MPLSLDPHELLOADJACENCYTYPE_LINK);
+ return SNMP_INTEGER(MPLSLDPHELLOADJACENCYTYPE_TARGETED);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#define LDP_LSRID_IDX_LEN 6
+#define LDP_ENTITY_IDX_LEN 1
+#define LDP_PEER_ENTRY_MAX_IDX_LEN 13
+
+static void ldpPeerTable_oid_to_index(
+ struct variable *v, oid name[],
+ size_t *length,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId)
+{
+ oid *offset = name + v->namelen;
+ int offsetlen = *length - v->namelen;
+ int len = offsetlen;
+
+ if (len > LDP_PEER_ENTRY_MAX_IDX_LEN)
+ len = LDP_PEER_ENTRY_MAX_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), entityLdpId);
+
+ offset += LDP_LSRID_IDX_LEN;
+ offsetlen -= LDP_LSRID_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_ENTITY_IDX_LEN)
+ len = LDP_ENTITY_IDX_LEN;
+
+ if (len >= LDP_ENTITY_IDX_LEN)
+ *entityIndex = offset[0];
+
+ offset += LDP_ENTITY_IDX_LEN;
+ offsetlen -= LDP_ENTITY_IDX_LEN;
+ len = offsetlen;
+
+ if (len > LDP_LSRID_IDX_LEN)
+ len = LDP_LSRID_IDX_LEN;
+
+ if (len >= LDP_LSRID_IDX_LEN)
+ oid2in_addr(offset, sizeof(struct in_addr), peerLdpId);
+}
+
+static struct ctl_nbr *
+ldpPeerTable_lookup_next(int first,
+ struct in_addr peerLdpId)
+{
+ struct nbr *nbr = NULL;
+ struct ctl_nbr *ctl_nbr = NULL;;
+
+ if (first)
+ nbr = nbr_get_first_ldpid();
+ else
+ nbr = nbr_get_next_ldpid(peerLdpId.s_addr);
+
+ if (nbr)
+ ctl_nbr = nbr_to_ctl(nbr);
+
+ return ctl_nbr;
+}
+
+static struct ctl_nbr *
+ldpPeerTable_lookup(struct variable *v, oid name[],
+ size_t *length, int exact,
+ struct in_addr *entityLdpId,
+ uint32_t *entityIndex,
+ struct in_addr *peerLdpId)
+{
+ struct ctl_nbr *ctl_nbr = NULL;
+ struct nbr *nbr = NULL;
+ int first = 0;
+
+ if (exact) {
+ if (*length < (long unsigned int)v->namelen
+ + LDP_PEER_ENTRY_MAX_IDX_LEN)
+ return NULL;
+
+ ldpPeerTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId);
+
+ nbr = nbr_find_ldpid(peerLdpId->s_addr);
+ if (nbr)
+ ctl_nbr = nbr_to_ctl(nbr);
+
+ return ctl_nbr;
+ } else {
+
+ int offsetlen = *length - v->namelen;
+ if (offsetlen < LDP_LSRID_IDX_LEN)
+ first = 1;
+
+ ldpPeerTable_oid_to_index(
+ v, name, length,
+ entityLdpId, entityIndex, peerLdpId);
+
+ ctl_nbr = ldpPeerTable_lookup_next(first, *peerLdpId);
+ return ctl_nbr;
+ }
+ return NULL;
+}
+
+static uint8_t *ldpPeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ struct ctl_nbr *ctl_nbr;
+
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ ctl_nbr = ldpPeerTable_lookup(v, name, length, exact, &entityLdpId,
+ &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ sizeof(struct in_addr));
+
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_addr(name + v->namelen + 7, &peerLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ /* Set length */
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPPEERLDPID:
+ *var_len = 6;
+ memcpy(snmp_ldp_rtrid, &ctl_nbr->id, IN_ADDR_SIZE);
+ return snmp_ldp_rtrid;
+ case MPLSLDPPEERLABELDISTMETHOD:
+ return SNMP_INTEGER(DOWNSTREAMUNSOLICITED);
+ case MPLSLDPPEERPATHVECTORLIMIT:
+ return SNMP_INTEGER(0);
+ case MPLSLDPPEERTRANSPORTADDRTYPE:
+ if (ctl_nbr->af == AF_INET)
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV4);
+ else
+ return SNMP_INTEGER(MPLSLDPPEERTRANSPORTADDRTYPE_IPV6);
+ case MPLSLDPPEERTRANSPORTADDR:
+ if (ctl_nbr->af == AF_INET) {
+ *var_len = sizeof(ctl_nbr->raddr.v4);
+ return ((uint8_t *)&ctl_nbr->raddr.v4);
+ } else {
+ *var_len = sizeof(ctl_nbr->raddr.v6);
+ return ((uint8_t *)&ctl_nbr->raddr.v6);
+ }
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+ struct ctl_nbr *ctl_nbr;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ ctl_nbr = ldpPeerTable_lookup(v, name, length, exact, &entityLdpId,
+ &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ sizeof(struct in_addr));
+
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_addr(name + v->namelen + 7, &peerLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ /* Set length */
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPSESSIONSTATELASTCHANGE:
+ *var_len = sizeof(time_t);
+ return (uint8_t *) &(ctl_nbr->uptime);
+ case MPLSLDPSESSIONSTATE:
+ switch (ctl_nbr->nbr_state) {
+ case NBR_STA_INITIAL:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_INITIALIZED);
+ case NBR_STA_OPENREC:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPENREC);
+ case NBR_STA_OPENSENT:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPENSENT);
+ case NBR_STA_OPER:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_OPERATIONAL);
+ default:
+ return SNMP_INTEGER(MPLSLDPSESSIONSTATE_NONEXISTENT);
+ }
+ case MPLSLDPSESSIONROLE:
+ if (ldp_addrcmp(ctl_nbr->af, &ctl_nbr->laddr, &ctl_nbr->raddr)
+ > 0)
+ return SNMP_INTEGER(MPLSLDPSESSIONROLE_ACTIVE);
+ else
+ return SNMP_INTEGER(MPLSLDPSESSIONROLE_PASSIVE);
+ case MPLSLDPSESSIONPROTOCOLVERSION:
+ return SNMP_INTEGER(LDP_VERSION);
+ case MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM:
+ return SNMP_INTEGER(ctl_nbr->hold_time_remaining);
+ case MPLSLDPSESSIONKEEPALIVETIME:
+ return SNMP_INTEGER(ctl_nbr->holdtime);
+ case MPLSLDPSESSIONMAXPDULENGTH:
+ if (ctl_nbr->nbr_state == NBR_STA_OPER)
+ return SNMP_INTEGER(ctl_nbr->max_pdu_len);
+ else
+ return SNMP_INTEGER(LDP_MAX_LEN);
+ case MPLSLDPSESSIONDISCONTINUITYTIME:
+ return SNMP_INTEGER(0); /* not supported */
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[],
+ size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ struct ctl_nbr *ctl_nbr = ldpPeerTable_lookup(v, name, length, exact,
+ &entityLdpId, &entityIndex, &peerLdpId);
+
+ if (!ctl_nbr)
+ return NULL;
+
+ if (!exact) {
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ /* Copy the name out */
+ memcpy(name, v->name, v->namelen * sizeof(oid));
+
+ /* Append index */
+ oid_copy_addr(name + v->namelen, &entityLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 4] = 0;
+ name[v->namelen + 5] = 0;
+ name[v->namelen + 6] = entityIndex;
+ oid_copy_addr(name + v->namelen + 7, &peerLdpId,
+ sizeof(struct in_addr));
+ name[v->namelen + 11] = 0;
+ name[v->namelen + 12] = 0;
+
+ *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN;
+ }
+
+ switch (v->magic) {
+ case MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS:
+ return SNMP_INTEGER(ctl_nbr->stats.unknown_msg);
+ case MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS:
+ return SNMP_INTEGER(ctl_nbr->stats.unknown_tlv);
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static struct variable ldpe_variables[] = {
+ {MPLS_LDP_LSR_ID, STRING, RONLY, ldpLsrId, 3, {1, 1, 1}},
+ {MPLS_LDP_LSR_LOOP_DETECTION_CAPABLE, INTEGER, RONLY,
+ ldpLoopDetectCap, 3, {1, 1, 2}},
+ {MPLS_LDP_ENTITY_LAST_CHANGE, TIMESTAMP, RONLY, ldpEntityLastChange,
+ 3, {1, 2, 1}},
+ {MPLS_LDP_ENTITY_INDEX_NEXT, UNSIGNED32, RONLY, ldpEntityIndexNext,
+ 3, {1, 2, 2}},
+
+ /* MPLS LDP mplsLdpEntityTable. */
+ {MPLSLDPENTITYLDPID, STRING, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 1}},
+ {MPLSLDPENTITYINDEX, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 2}},
+ {MPLSLDPENTITYPROTOCOLVERSION, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 3}},
+ {MPLSLDPENTITYADMINSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 4}},
+ {MPLSLDPENTITYOPERSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 5}},
+ {MPLSLDPENTITYTCPPORT, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 6}},
+ {MPLSLDPENTITYUDPDSCPORT, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 7}},
+ {MPLSLDPENTITYMAXPDULENGTH, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 8}},
+ {MPLSLDPENTITYKEEPALIVEHOLDTIMER, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 9}},
+ {MPLSLDPENTITYHELLOHOLDTIMER, UNSIGNED32, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 10}},
+ {MPLSLDPENTITYINITSESSIONTHRESHOLD, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 11}},
+ {MPLSLDPENTITYLABELDISTMETHOD, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 12}},
+ {MPLSLDPENTITYLABELRETENTIONMODE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 13}},
+ {MPLSLDPENTITYPATHVECTORLIMIT, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 14}},
+ {MPLSLDPENTITYHOPCOUNTLIMIT, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 15}},
+ {MPLSLDPENTITYTRANSPORTADDRKIND, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 16}},
+ {MPLSLDPENTITYTARGETPEER, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 17}},
+ {MPLSLDPENTITYTARGETPEERADDRTYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 18}},
+ {MPLSLDPENTITYTARGETPEERADDR, STRING, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 19}},
+ {MPLSLDPENTITYLABELTYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 20}},
+ {MPLSLDPENTITYDISCONTINUITYTIME, TIMESTAMP, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 21}},
+ {MPLSLDPENTITYSTORAGETYPE, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 22}},
+ {MPLSLDPENTITYROWSTATUS, INTEGER, RONLY, ldpEntityTable,
+ 5, {1, 2, 3, 1, 23}},
+
+ /* MPLS LDP mplsLdpEntityStatsTable. */
+ { MPLSLDPENTITYSTATSSESSIONATTEMPTS, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 1}},
+ { MPLSLDPENTITYSTATSSESSIONREJHELLO, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 2}},
+ { MPLSLDPENTITYSTATSSESSIONREJAD, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 3}},
+ { MPLSLDPENTITYSTATSSESSIONREJMAXPDU, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 4}},
+ { MPLSLDPENTITYSTATSSESSIONREJLR, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 5}},
+ { MPLSLDPENTITYSTATSBADLDPID, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 6}},
+ { MPLSLDPENTITYSTATSBADPDULENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 7}},
+ { MPLSLDPENTITYSTATSBADMSGLENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 8}},
+ { MPLSLDPENTITYSTATSBADTLVLENGTH, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 9}},
+ { MPLSLDPENTITYSTATSMALFORMEDTLV, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 10}},
+ { MPLSLDPENTITYSTATSKEEPALIVEEXP, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 11}},
+ { MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 12}},
+ { MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY, COUNTER32, RONLY,
+ ldpEntityStatsTable, 5, {1, 2, 4, 1, 13}},
+
+ /* MPLS LDP mplsLdpPeerTable */
+ {MPLSLDPPEERLDPID, STRING, RONLY, ldpPeerTable, 5, {1, 3, 2, 1, 1}},
+ {MPLSLDPPEERLABELDISTMETHOD, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 2}},
+ {MPLSLDPPEERPATHVECTORLIMIT, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 3}},
+ {MPLSLDPPEERTRANSPORTADDRTYPE, INTEGER, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 4}},
+ {MPLSLDPPEERTRANSPORTADDR, STRING, RONLY, ldpPeerTable,
+ 5, {1, 3, 2, 1, 5}},
+
+ /* MPLS LDP mplsLdpSessionTable */
+ {MPLSLDPSESSIONSTATELASTCHANGE, TIMESTAMP, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 1}},
+ {MPLSLDPSESSIONSTATE, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 2}},
+ {MPLSLDPSESSIONROLE, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 3}},
+ {MPLSLDPSESSIONPROTOCOLVERSION, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 4}},
+ {MPLSLDPSESSIONKEEPALIVEHOLDTIMEREM, INTEGER, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 5}},
+ {MPLSLDPSESSIONKEEPALIVETIME, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 6}},
+ {MPLSLDPSESSIONMAXPDULENGTH, UNSIGNED32, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 7}},
+ {MPLSLDPSESSIONDISCONTINUITYTIME, TIMESTAMP, RONLY, ldpSessionTable,
+ 5, {1, 3, 3, 1, 8}},
+
+ /* MPLS LDP mplsLdpSessionStatsTable */
+ {MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS, COUNTER32, RONLY,
+ ldpSessionStatsTable, 5, {1, 3, 4, 1, 1}},
+ {MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS, COUNTER32, RONLY,
+ ldpSessionStatsTable, 5, {1, 3, 4, 1, 2}},
+
+ /* MPLS LDP mplsLdpHelloAdjacencyTable. */
+ {MPLSLDPHELLOADJACENCYINDEX, UNSIGNED32, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 1}},
+ {MPLSLDPHELLOADJACENCYHOLDTIMEREM, INTEGER, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 2}},
+ {MPLSLDPHELLOADJACENCYHOLDTIME, UNSIGNED32, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 3}},
+ {MPLSLDPHELLOADJACENCYTYPE, INTEGER, RONLY,
+ ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 4}},
+};
+
+static struct variable lde_variables[] = {
+};
+
+static struct trap_object ldpSessionTrapList[] = {
+ {5, {1, 3, 3, 1, MPLSLDPSESSIONSTATE}},
+ {5, {1, 3, 3, 1, MPLSLDPSESSIONDISCONTINUITYTIME}},
+ {5, {1, 3, 4, 1, MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS}},
+ {5, {1, 3, 4, 1, MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS}}};
+
+/* LDP TRAP. */
+#define LDPINITSESSIONTHRESHOLDEXCEEDED 1
+#define LDPPATHVECTORLIMITMISMATCH 2
+#define LDPSESSIONUP 3
+#define LDPSESSIONDOWN 4
+
+static void
+ldpTrapSession(struct nbr * nbr, unsigned int sptrap)
+{
+ oid index[sizeof(oid) * (LDP_PEER_ENTRY_MAX_IDX_LEN + 1)];
+
+ struct in_addr entityLdpId = {.s_addr = 0};
+ uint32_t entityIndex = 0;
+ struct in_addr peerLdpId = {.s_addr = 0};
+
+ struct ctl_nbr *ctl_nbr = nbr_to_ctl(nbr);
+
+ entityLdpId.s_addr = ldp_rtr_id_get(leconf);
+ entityIndex = LDP_DEFAULT_ENTITY_INDEX;
+ peerLdpId = ctl_nbr->id;
+
+ oid_copy_addr(index, &entityLdpId, sizeof(struct in_addr));
+ index[4] = 0;
+ index[5] = 0;
+ index[6] = entityIndex;
+ oid_copy_addr(&index[7], &peerLdpId, sizeof(struct in_addr));
+ index[11] = 0;
+ index[12] = 0;
+
+ index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0;
+
+ smux_trap(ldpe_variables, array_size(ldpe_variables), ldp_trap_oid,
+ array_size(ldp_trap_oid), ldp_oid,
+ sizeof(ldp_oid) / sizeof(oid), index,
+ LDP_PEER_ENTRY_MAX_IDX_LEN + 1,
+ ldpSessionTrapList, array_size(ldpSessionTrapList), sptrap);
+}
+
+static void
+ldpTrapSessionUp(struct nbr * nbr)
+{
+ ldpTrapSession(nbr, LDPSESSIONUP);
+}
+
+static void
+ldpTrapSessionDown(struct nbr * nbr)
+{
+ ldpTrapSession(nbr, LDPSESSIONDOWN);
+}
+
+static int ldp_snmp_agentx_enabled()
+{
+ main_imsg_compose_both(IMSG_AGENTX_ENABLED, NULL, 0);
+
+ return 0;
+}
+
+static int ldp_snmp_nbr_state_change(struct nbr * nbr, int old_state)
+{
+ if (old_state == nbr->state)
+ return 0;
+
+ if (nbr->state == NBR_STA_OPER)
+ ldpTrapSessionUp(nbr);
+ else if (old_state == NBR_STA_OPER)
+ ldpTrapSessionDown(nbr);
+
+ return 0;
+}
+
+static int ldp_snmp_init(struct thread_master *tm)
+{
+ hook_register(agentx_enabled, ldp_snmp_agentx_enabled);
+
+ smux_init(tm);
+
+ return 0;
+}
+
+static int ldp_snmp_register_mib(struct thread_master *tm)
+{
+ static int registered = 0;
+
+ if (registered)
+ return 0;
+
+ registered = 1;
+
+ smux_init(tm);
+
+ smux_agentx_enable();
+
+ if (ldpd_process == PROC_LDE_ENGINE)
+ REGISTER_MIB("mibII/ldp", lde_variables, variable, ldp_oid);
+ else if (ldpd_process == PROC_LDP_ENGINE) {
+ REGISTER_MIB("mibII/ldp", ldpe_variables, variable, ldp_oid);
+
+ hook_register(ldp_nbr_state_change, ldp_snmp_nbr_state_change);
+ }
+
+ return 0;
+}
+
+static int ldp_snmp_module_init(void)
+{
+ if (ldpd_process == PROC_MAIN)
+ hook_register(frr_late_init, ldp_snmp_init);
+ else
+ hook_register(ldp_register_mib, ldp_snmp_register_mib);
+
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "ldp_snmp", .version = FRR_VERSION,
+ .description = "ldp AgentX SNMP module",
+ .init = ldp_snmp_module_init, )
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index 83e93ebbbc..14235a0f1f 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -86,6 +86,30 @@ static struct imsgev *iev_lde, *iev_lde_sync;
static pid_t ldpe_pid;
static pid_t lde_pid;
+static struct frr_daemon_info ldpd_di;
+
+DEFINE_HOOK(ldp_register_mib, (struct thread_master * tm), (tm))
+
+static void ldp_load_module(const char *name)
+{
+ const char *dir;
+ dir = ldpd_di.module_path ? ldpd_di.module_path : frr_moduledir;
+ char moderr[256];
+ struct frrmod_runtime *module;
+
+ module = frrmod_load(name, dir, moderr, sizeof(moderr));
+ if (!module) {
+ fprintf(stderr, "%s: failed to load %s", __func__, name);
+ log_warnx("%s: failed to load %s", __func__, name);
+ }
+}
+
+void ldp_agentx_enabled(void)
+{
+ ldp_load_module("snmp");
+ hook_call(ldp_register_mib, master);
+}
+
enum ldpd_process ldpd_process;
#define LDP_DEFAULT_CONFIG "ldpd.conf"
@@ -94,8 +118,6 @@ enum ldpd_process ldpd_process;
/* Master of threads. */
struct thread_master *master;
-static struct frr_daemon_info ldpd_di;
-
/* ldpd privileges */
static zebra_capabilities_t _caps_p [] =
{
@@ -1343,6 +1365,9 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf)
ldpe_reset_ds_nbrs();
}
+ if (ldpd_process == PROC_LDP_ENGINE)
+ ldpe_set_config_change_time();
+
conf->flags = xconf->flags;
}
diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h
index beb625d8a2..73c81349ce 100644
--- a/ldpd/ldpd.h
+++ b/ldpd/ldpd.h
@@ -161,6 +161,7 @@ enum imsg_type {
IMSG_RLFA_REG,
IMSG_RLFA_UNREG_ALL,
IMSG_RLFA_LABELS,
+ IMSG_AGENTX_ENABLED,
};
struct ldpd_init {
@@ -434,6 +435,25 @@ struct ldp_stats {
uint32_t labelrel_rcvd;
uint32_t labelabreq_sent;
uint32_t labelabreq_rcvd;
+ uint32_t unknown_tlv;
+ uint32_t unknown_msg;
+
+};
+
+struct ldp_entity_stats {
+ uint32_t session_attempts;
+ uint32_t session_rejects_hello;
+ uint32_t session_rejects_ad;
+ uint32_t session_rejects_max_pdu;
+ uint32_t session_rejects_lr;
+ uint32_t bad_ldp_id;
+ uint32_t bad_pdu_len;
+ uint32_t bad_msg_len;
+ uint32_t bad_tlv_len;
+ uint32_t malformed_tlv;
+ uint32_t keepalive_timer_exp;
+ uint32_t shutdown_rcv_notify;
+ uint32_t shutdown_send_notify;
};
struct l2vpn_if {
@@ -562,6 +582,8 @@ struct ldpd_conf {
uint16_t trans_pref;
uint16_t wait_for_sync_interval;
int flags;
+ time_t config_change_time;
+ struct ldp_entity_stats stats;
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(ldpd_conf)
@@ -683,6 +705,8 @@ struct ctl_nbr {
int nbr_state;
struct ldp_stats stats;
int flags;
+ uint16_t max_pdu_len;
+ uint16_t hold_time_remaining;
};
struct ctl_rt {
@@ -891,4 +915,8 @@ int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_INTFACELOCAL))
#endif
+DECLARE_HOOK(ldp_register_mib, (struct thread_master * tm), (tm))
+
+extern void ldp_agentx_enabled(void);
+
#endif /* _LDPD_H_ */
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index 6a5a0750bd..d09eb2fa33 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -33,6 +33,7 @@
#include "memory.h"
#include "privs.h"
#include "sigevent.h"
+#include "libfrr.h"
static void ldpe_shutdown(void);
static int ldpe_dispatch_main(struct thread *);
@@ -103,15 +104,13 @@ char *pkt_ptr; /* packet buffer */
void
ldpe(void)
{
- struct thread thread;
-
#ifdef HAVE_SETPROCTITLE
setproctitle("ldp engine");
#endif
ldpd_process = PROC_LDP_ENGINE;
log_procname = log_procnames[ldpd_process];
- master = thread_master_create(NULL);
+ master = frr_init();
/* setup signal handler */
signal_init(master, array_size(ldpe_signals), ldpe_signals);
@@ -133,9 +132,12 @@ ldpe(void)
/* create base configuration */
leconf = config_new_empty();
- /* Fetch next active thread. */
+ struct thread thread;
while (thread_fetch(master, &thread))
thread_call(&thread);
+
+ /* NOTREACHED */
+ return;
}
void
@@ -387,6 +389,9 @@ ldpe_dispatch_main(struct thread *thread)
memcpy(&init, imsg.data, sizeof(init));
ldpe_init(&init);
break;
+ case IMSG_AGENTX_ENABLED:
+ ldp_agentx_enabled();
+ break;
case IMSG_CLOSE_SOCKETS:
af = imsg.hdr.peerid;
@@ -1073,3 +1078,10 @@ ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf,
if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0)
ldpe_remove_dynamic_tnbrs(af);
}
+
+void
+ldpe_set_config_change_time(void)
+{
+ /* SNMP update time when ever there is a config change */
+ leconf->config_change_time = time(NULL);
+}
diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h
index ef4702341b..9572f1ac12 100644
--- a/ldpd/ldpe.h
+++ b/ldpd/ldpe.h
@@ -216,6 +216,7 @@ void ldpe_nbr_ctl(struct ctl_conn *);
void ldpe_ldp_sync_ctl(struct ctl_conn *);
void mapping_list_add(struct mapping_head *, struct map *);
void mapping_list_clr(struct mapping_head *);
+void ldpe_set_config_change_time(void);
/* interface.c */
struct iface *if_new(const char *);
@@ -266,6 +267,8 @@ struct nbr *nbr_new(struct in_addr, int, int, union ldpd_addr *,
uint32_t);
void nbr_del(struct nbr *);
struct nbr *nbr_find_ldpid(uint32_t);
+struct nbr *nbr_get_first_ldpid(void);
+struct nbr *nbr_get_next_ldpid(uint32_t);
struct nbr *nbr_find_addr(int, union ldpd_addr *);
struct nbr *nbr_find_peerid(uint32_t);
int nbr_adj_count(struct nbr *, int);
@@ -318,4 +321,6 @@ void ldpe_l2vpn_exit(struct l2vpn *);
void ldpe_l2vpn_pw_init(struct l2vpn_pw *);
void ldpe_l2vpn_pw_exit(struct l2vpn_pw *);
+DECLARE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state), (nbr, old_state))
+
#endif /* _LDPE_H_ */
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
index 75deaad2c0..23c67ec1ca 100644
--- a/ldpd/neighbor.c
+++ b/ldpd/neighbor.c
@@ -26,6 +26,8 @@
#include "lde.h"
#include "log.h"
+DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state), (nbr, old_state))
+
static __inline int nbr_id_compare(const struct nbr *, const struct nbr *);
static __inline int nbr_addr_compare(const struct nbr *,
const struct nbr *);
@@ -158,6 +160,8 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event)
&nbr->id, nbr_state_name(old_state),
nbr_state_name(nbr->state));
+ hook_call(ldp_nbr_state_change, nbr, old_state);
+
if (nbr->state == NBR_STA_OPER) {
gettimeofday(&now, NULL);
nbr->uptime = now.tv_sec;
@@ -355,6 +359,23 @@ nbr_find_ldpid(uint32_t lsr_id)
}
struct nbr *
+nbr_get_first_ldpid()
+{
+ return (RB_MIN(nbr_id_head, &nbrs_by_id));
+}
+
+struct nbr *
+nbr_get_next_ldpid(uint32_t lsr_id)
+{
+ struct nbr *nbr;
+ nbr = nbr_find_ldpid(lsr_id);
+ if (nbr)
+ return (RB_NEXT(nbr_id_head, nbr));
+ return NULL;
+}
+
+
+struct nbr *
nbr_find_addr(int af, union ldpd_addr *addr)
{
struct nbr n;
@@ -831,14 +852,20 @@ nbr_to_ctl(struct nbr *nbr)
nctl.af = nbr->af;
nctl.id = nbr->id;
nctl.laddr = nbr->laddr;
- nctl.lport = nbr->tcp->lport;
+ nctl.lport = nbr->tcp ? nbr->tcp->lport : 0;
nctl.raddr = nbr->raddr;
- nctl.rport = nbr->tcp->rport;
+ nctl.rport = nbr->tcp ? nbr->tcp->rport : 0;
nctl.auth_method = nbr->auth.method;
nctl.holdtime = nbr->keepalive;
nctl.nbr_state = nbr->state;
nctl.stats = nbr->stats;
nctl.flags = nbr->flags;
+ nctl.max_pdu_len = nbr->max_pdu_len;
+ if (nbr->keepalive_timer)
+ nctl.hold_time_remaining =
+ thread_timer_remain_second(nbr->keepalive_timer);
+ else
+ nctl.hold_time_remaining = 0;
gettimeofday(&now, NULL);
if (nbr->state == NBR_STA_OPER) {
diff --git a/ldpd/notification.c b/ldpd/notification.c
index f84e0f893b..3ecf5d4ba5 100644
--- a/ldpd/notification.c
+++ b/ldpd/notification.c
@@ -69,6 +69,36 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
tcp->nbr->stats.notif_sent++;
}
+ /* update SNMP session counters */
+ switch (nm->status_code) {
+ case S_NO_HELLO:
+ leconf->stats.session_rejects_hello++;
+ break;
+ case S_BAD_LDP_ID:
+ leconf->stats.bad_ldp_id++;
+ break;
+ case S_BAD_PDU_LEN:
+ leconf->stats.bad_pdu_len++;
+ break;
+ case S_BAD_MSG_LEN:
+ leconf->stats.bad_msg_len++;
+ break;
+ case S_BAD_TLV_LEN:
+ leconf->stats.bad_tlv_len++;
+ break;
+ case S_BAD_TLV_VAL:
+ leconf->stats.malformed_tlv++;
+ break;
+ case S_KEEPALIVE_TMR:
+ leconf->stats.keepalive_timer_exp++;
+ break;
+ case S_SHUTDOWN:
+ leconf->stats.shutdown_send_notify++;
+ break;
+ default:
+ break;
+ }
+
evbuf_enqueue(&tcp->wbuf, buf);
}
@@ -122,6 +152,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
if (len < STATUS_SIZE) {
session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
+ leconf->stats.bad_msg_len++;
return (-1);
}
memcpy(&st, buf, sizeof(st));
@@ -129,6 +160,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
ntohs(st.length) > len - TLV_HDR_SIZE) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
return (-1);
}
buf += STATUS_SIZE;
@@ -145,6 +177,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
if (len < sizeof(tlv)) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
return (-1);
}
@@ -153,6 +186,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
tlv_len = ntohs(tlv.length);
if (tlv_len + TLV_HDR_SIZE > len) {
session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
return (-1);
}
buf += TLV_HDR_SIZE;
@@ -182,14 +216,17 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
if (tlen != tlv_len) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
+ leconf->stats.bad_tlv_len++;
return (-1);
}
nm.flags |= F_NOTIF_FEC;
break;
default:
- if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) {
+ nbr->stats.unknown_tlv++;
send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
msg.id, msg.type, tlv_type, tlv_len, buf);
+ }
/* ignore unknown tlv */
break;
}
@@ -243,21 +280,57 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
* initialization, it SHOULD transmit a Shutdown message and
* then close the transport connection".
*/
- if (nbr->state != NBR_STA_OPER && nm.status_code == S_SHUTDOWN)
+ if (nbr->state != NBR_STA_OPER &&
+ nm.status_code == S_SHUTDOWN) {
+ leconf->stats.session_attempts++;
send_notification(nbr->tcp, S_SHUTDOWN,
msg.id, msg.type);
+ }
+ leconf->stats.shutdown_rcv_notify++;
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
return (-1);
}
- /* lde needs to know about a few notification messages */
+ /* lde needs to know about a few notification messages
+ * and update SNMP session counters
+ */
switch (nm.status_code) {
case S_PW_STATUS:
case S_ENDOFLIB:
ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
&nm, sizeof(nm));
break;
+ case S_NO_HELLO:
+ leconf->stats.session_rejects_hello++;
+ break;
+ case S_PARM_ADV_MODE:
+ leconf->stats.session_rejects_ad++;
+ break;
+ case S_MAX_PDU_LEN:
+ leconf->stats.session_rejects_max_pdu++;
+ break;
+ case S_PARM_L_RANGE:
+ leconf->stats.session_rejects_lr++;
+ break;
+ case S_BAD_LDP_ID:
+ leconf->stats.bad_ldp_id++;
+ break;
+ case S_BAD_PDU_LEN:
+ leconf->stats.bad_pdu_len++;
+ break;
+ case S_BAD_MSG_LEN:
+ leconf->stats.bad_msg_len++;
+ break;
+ case S_BAD_TLV_LEN:
+ leconf->stats.bad_tlv_len++;
+ break;
+ case S_BAD_TLV_VAL:
+ leconf->stats.malformed_tlv++;
+ break;
+ case S_SHUTDOWN:
+ leconf->stats.shutdown_rcv_notify++;
+ break;
default:
break;
}
diff --git a/ldpd/packet.c b/ldpd/packet.c
index fdcaa79d23..8735faf3dd 100644
--- a/ldpd/packet.c
+++ b/ldpd/packet.c
@@ -560,9 +560,11 @@ session_read(struct thread *thread)
default:
log_debug("%s: unknown LDP message from nbr %pI4",
__func__, &nbr->id);
- if (!(ntohs(msg->type) & UNKNOWN_FLAG))
+ if (!(ntohs(msg->type) & UNKNOWN_FLAG)) {
+ nbr->stats.unknown_msg++;
send_notification(nbr->tcp,
S_UNKNOWN_MSG, msg->id, msg->type);
+ }
/* ignore the message */
ret = 0;
break;
@@ -667,6 +669,12 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id,
case NBR_STA_INITIAL:
case NBR_STA_OPENREC:
case NBR_STA_OPENSENT:
+ /* update SNMP session counters during initialization */
+ leconf->stats.session_attempts++;
+ send_notification(nbr->tcp, status, msg_id, msg_type);
+
+ nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
+ break;
case NBR_STA_OPER:
send_notification(nbr->tcp, status, msg_id, msg_type);
diff --git a/ldpd/subdir.am b/ldpd/subdir.am
index d89d18341d..b01d414de8 100644
--- a/ldpd/subdir.am
+++ b/ldpd/subdir.am
@@ -41,6 +41,10 @@ ldpd_libldp_a_SOURCES = \
ldpd/util.c \
# end
+if SNMP
+module_LTLIBRARIES += ldpd/ldpd_snmp.la
+endif
+
clippy_scan += \
ldpd/ldp_vty_cmds.c \
# end
@@ -59,3 +63,8 @@ noinst_HEADERS += \
ldpd_ldpd_SOURCES = ldpd/ldpd.c
ldpd_ldpd_LDADD = ldpd/libldp.a lib/libfrr.la $(LIBCAP)
+
+ldpd_ldpd_snmp_la_SOURCES = ldpd/ldp_snmp.c
+ldpd_ldpd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
+ldpd_ldpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ldpd_ldpd_snmp_la_LIBADD = lib/libfrrsnmp.la
diff --git a/lib/agentx.c b/lib/agentx.c
index c8d7d75a81..c1ff7a61b1 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -246,6 +246,11 @@ DEFUN (no_agentx,
return CMD_WARNING_CONFIG_FAILED;
}
+int smux_enabled(void)
+{
+ return agentx_enabled;
+}
+
void smux_init(struct thread_master *tm)
{
agentx_tm = tm;
@@ -392,4 +397,9 @@ int smux_trap_multi_index(struct variable *vp, size_t vp_len, const oid *ename,
return 1;
}
+void smux_events_update(void)
+{
+ agentx_events_update();
+}
+
#endif /* SNMP_AGENTX */
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
index 2007b37cdf..c83738e729 100644
--- a/lib/filter_nb.c
+++ b/lib/filter_nb.c
@@ -120,6 +120,101 @@ static void prefix_list_entry_set_empty(struct prefix_list_entry *ple)
ple->le = 0;
}
+static int
+prefix_list_nb_validate_v4_af_type(const struct lyd_node *plist_dnode,
+ char *errmsg, size_t errmsg_len)
+{
+ int af_type;
+
+ af_type = yang_dnode_get_enum(plist_dnode, "./type");
+ if (af_type != YPLT_IPV4) {
+ snprintf(errmsg, errmsg_len,
+ "prefix-list type %u is mismatched.", af_type);
+ return NB_ERR_VALIDATION;
+ }
+
+ return NB_OK;
+}
+
+static int
+prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode,
+ char *errmsg, size_t errmsg_len)
+{
+ int af_type;
+
+ af_type = yang_dnode_get_enum(plist_dnode, "./type");
+ if (af_type != YPLT_IPV6) {
+ snprintf(errmsg, errmsg_len,
+ "prefix-list type %u is mismatched.", af_type);
+ return NB_ERR_VALIDATION;
+ }
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->le = yang_dnode_get_uint8(args->dnode, NULL);
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->ge = 0;
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->le = 0;
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
/**
* Unsets the cisco style rule for addresses so it becomes disabled (the
* equivalent of setting: `0.0.0.0/32`).
@@ -308,45 +403,6 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda)
return pda->pda_found;
}
-static bool plist_is_dup_nb(const struct lyd_node *dnode)
-{
- const struct lyd_node *entry_dnode =
- yang_dnode_get_parent(dnode, "entry");
- struct plist_dup_args pda = {};
- int idx = 0, arg_idx = 0;
- static const char *entries[] = {
- "./ipv4-prefix",
- "./ipv4-prefix-length-greater-or-equal",
- "./ipv4-prefix-length-lesser-or-equal",
- "./ipv6-prefix",
- "./ipv6-prefix-length-greater-or-equal",
- "./ipv6-prefix-length-lesser-or-equal",
- "./any",
- NULL
- };
-
- /* Initialize. */
- pda.pda_type = yang_dnode_get_string(entry_dnode, "../type");
- pda.pda_name = yang_dnode_get_string(entry_dnode, "../name");
- pda.pda_entry_dnode = entry_dnode;
-
- /* Load all values/XPaths. */
- while (entries[idx] != NULL) {
- if (!yang_dnode_exists(entry_dnode, entries[idx])) {
- idx++;
- continue;
- }
-
- pda.pda_xpath[arg_idx] = entries[idx];
- pda.pda_value[arg_idx] =
- yang_dnode_get_string(entry_dnode, entries[idx]);
- arg_idx++;
- idx++;
- }
-
- return plist_is_dup(entry_dnode, &pda);
-}
-
/*
* XPath: /frr-filter:lib/access-list
*/
@@ -1148,25 +1204,11 @@ static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
-/*
- * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix
- */
-static int
-lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
+static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args)
{
struct prefix_list_entry *ple;
struct prefix p;
- if (args->event == NB_EV_VALIDATE) {
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
- return NB_OK;
- }
-
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -1193,8 +1235,7 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
-static int
-lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
+static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args)
{
struct prefix_list_entry *ple;
@@ -1215,6 +1256,61 @@ lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
}
/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix
+ */
+static int
+lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
+{
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
+
+ return prefix_list_nb_validate_v4_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
+
+ return lib_prefix_list_entry_prefix_modify(args);
+}
+
+static int
+lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
+{
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ return lib_prefix_list_entry_prefix_destroy(args);
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix
+ */
+static int
+lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args)
+{
+
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
+
+ return prefix_list_nb_validate_v6_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
+
+ return lib_prefix_list_entry_prefix_modify(args);
+}
+
+static int
+lib_prefix_list_entry_ipv6_prefix_destroy(struct nb_cb_destroy_args *args)
+{
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ return lib_prefix_list_entry_prefix_destroy(args);
+}
+
+/*
* XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal
*/
static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
@@ -1226,16 +1322,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
- if (args->event == NB_EV_VALIDATE) {
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
- return NB_OK;
- }
-
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -1255,44 +1341,72 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy(
struct nb_cb_destroy_args *args)
{
- struct prefix_list_entry *ple;
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
- if (args->event != NB_EV_APPLY)
- return NB_OK;
+ return prefix_list_nb_validate_v4_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
- ple = nb_running_get_entry(args->dnode, NULL, true);
+ return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy(
+ args);
+}
- /* Start prefix entry update procedure. */
- prefix_list_entry_update_start(ple);
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal
+ */
+static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
+ struct nb_cb_modify_args *args)
+{
+ if (args->event == NB_EV_VALIDATE
+ && prefix_list_length_validate(args) != NB_OK)
+ return NB_ERR_VALIDATION;
- ple->ge = 0;
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
+ return prefix_list_nb_validate_v4_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
- return NB_OK;
+ return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args);
+}
+
+static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
+
+ return prefix_list_nb_validate_v4_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
+
+ return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy(
+ args);
}
/*
- * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal
*/
-static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
+static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify(
struct nb_cb_modify_args *args)
{
struct prefix_list_entry *ple;
- if (args->event == NB_EV_VALIDATE &&
- prefix_list_length_validate(args) != NB_OK)
+ if (args->event == NB_EV_VALIDATE
+ && prefix_list_length_validate(args) != NB_OK)
return NB_ERR_VALIDATION;
if (args->event == NB_EV_VALIDATE) {
- if (plist_is_dup_nb(args->dnode)) {
- snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
- return NB_ERR_VALIDATION;
- }
- return NB_OK;
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
+
+ return prefix_list_nb_validate_v6_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
}
if (args->event != NB_EV_APPLY)
@@ -1303,7 +1417,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
/* Start prefix entry update procedure. */
prefix_list_entry_update_start(ple);
- ple->le = yang_dnode_get_uint8(args->dnode, NULL);
+ ple->ge = yang_dnode_get_uint8(args->dnode, NULL);
/* Finish prefix entry update procedure. */
prefix_list_entry_update_finish(ple);
@@ -1311,45 +1425,71 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
return NB_OK;
}
-static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy(
+static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy(
struct nb_cb_destroy_args *args)
{
- struct prefix_list_entry *ple;
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
- if (args->event != NB_EV_APPLY)
- return NB_OK;
+ return prefix_list_nb_validate_v6_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
- ple = nb_running_get_entry(args->dnode, NULL, true);
+ return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy(
+ args);
+}
- /* Start prefix entry update procedure. */
- prefix_list_entry_update_start(ple);
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal
+ */
+static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify(
+ struct nb_cb_modify_args *args)
+{
+ if (args->event == NB_EV_VALIDATE
+ && prefix_list_length_validate(args) != NB_OK)
+ return NB_ERR_VALIDATION;
- ple->le = 0;
+ if (args->event == NB_EV_VALIDATE) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
- /* Finish prefix entry update procedure. */
- prefix_list_entry_update_finish(ple);
+ return prefix_list_nb_validate_v6_af_type(
+ plist_dnode, args->errmsg, args->errmsg_len);
+ }
- return NB_OK;
+ return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args);
}
-/*
- * XPath: /frr-filter:lib/prefix-list/entry/any
- */
-static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
+static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
{
- struct prefix_list_entry *ple;
- int type;
+ int af_type;
if (args->event == NB_EV_VALIDATE) {
- if (plist_is_dup_nb(args->dnode)) {
+ const struct lyd_node *plist_dnode =
+ yang_dnode_get_parent(args->dnode, "prefix-list");
+ af_type = yang_dnode_get_enum(plist_dnode, "./type");
+ if (af_type != YPLT_IPV6) {
snprintf(args->errmsg, args->errmsg_len,
- "duplicated prefix list value: %s",
- yang_dnode_get_string(args->dnode, NULL));
+ "prefix-list type %u is mismatched.", af_type);
return NB_ERR_VALIDATION;
}
return NB_OK;
}
+ return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy(
+ args);
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/any
+ */
+static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
+{
+ struct prefix_list_entry *ple;
+ int type;
+
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -1583,22 +1723,22 @@ const struct frr_yang_module_info frr_filter_info = {
{
.xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix",
.cbs = {
- .modify = lib_prefix_list_entry_ipv4_prefix_modify,
- .destroy = lib_prefix_list_entry_ipv4_prefix_destroy,
+ .modify = lib_prefix_list_entry_ipv6_prefix_modify,
+ .destroy = lib_prefix_list_entry_ipv6_prefix_destroy,
}
},
{
.xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal",
.cbs = {
- .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify,
- .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy,
+ .modify = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy,
}
},
{
.xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal",
.cbs = {
- .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify,
- .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy,
+ .modify = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy,
}
},
{
diff --git a/lib/northbound.c b/lib/northbound.c
index 224951b22b..b6d3518285 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -1817,6 +1817,16 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
/* Find the list entry pointer. */
nn = dn->schema->priv;
+ if (!nn->cbs.lookup_entry) {
+ flog_warn(
+ EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: data path doesn't support iteration over operational data: %s",
+ __func__, xpath);
+ list_delete(&list_dnodes);
+ yang_dnode_free(dnode);
+ return NB_ERR;
+ }
+
list_entry =
nb_callback_lookup_entry(nn, list_entry, &list_keys);
if (list_entry == NULL) {
diff --git a/lib/plist.c b/lib/plist.c
index 4588dfe1d3..fe4689becd 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -684,6 +684,7 @@ void prefix_list_entry_update_start(struct prefix_list_entry *ple)
if (pl->head || pl->tail || pl->desc)
pl->master->recent = pl;
+ ple->next_best = NULL;
ple->installed = false;
}
diff --git a/lib/smux.h b/lib/smux.h
index a263478a2e..57128b7928 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -103,6 +103,8 @@ struct index_oid {
#define SNMP_IP6ADDRESS(V) (*var_len = sizeof(struct in6_addr), (uint8_t *)&V)
+extern int smux_enabled(void);
+
extern void smux_init(struct thread_master *tm);
extern void smux_agentx_enable(void);
extern void smux_register_mib(const char *, struct variable *, size_t, int,
@@ -143,6 +145,8 @@ extern int smux_trap_multi_index(struct variable *vp, size_t vp_len,
struct index_oid *iname, size_t index_len,
const struct trap_object *trapobj,
size_t trapobjlen, uint8_t sptrap);
+
+extern void smux_events_update(void);
extern int oid_compare(const oid *, int, const oid *, int);
extern void oid2in_addr(oid[], int, struct in_addr *);
extern void oid2in6_addr(oid oid[], struct in6_addr *addr);
diff --git a/lib/vty.c b/lib/vty.c
index df518422f0..4cefb5e80c 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1783,7 +1783,6 @@ static int vty_accept(struct thread *thread)
int accept_sock;
struct prefix p;
struct access_list *acl = NULL;
- char buf[SU_ADDRSTRLEN];
accept_sock = THREAD_FD(thread);
@@ -1804,8 +1803,8 @@ static int vty_accept(struct thread *thread)
if (!sockunion2hostprefix(&su, &p)) {
close(vty_sock);
- zlog_info("Vty unable to convert prefix from sockunion %s",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ zlog_info("Vty unable to convert prefix from sockunion %pSU",
+ &su);
return -1;
}
@@ -1813,8 +1812,7 @@ static int vty_accept(struct thread *thread)
if (p.family == AF_INET && vty_accesslist_name) {
if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
- zlog_info("Vty connection refused from %s",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ zlog_info("Vty connection refused from %pSU", &su);
close(vty_sock);
/* continue accepting connections */
@@ -1829,8 +1827,7 @@ static int vty_accept(struct thread *thread)
if ((acl = access_list_lookup(AFI_IP6,
vty_ipv6_accesslist_name))
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
- zlog_info("Vty connection refused from %s",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ zlog_info("Vty connection refused from %pSU", &su);
close(vty_sock);
/* continue accepting connections */
@@ -1847,8 +1844,7 @@ static int vty_accept(struct thread *thread)
zlog_info("can't set sockopt to vty_sock : %s",
safe_strerror(errno));
- zlog_info("Vty connection from %s",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ zlog_info("Vty connection from %pSU", &su);
vty_create(vty_sock, &su);
@@ -2325,7 +2321,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp)
nl = strchr(ve->error_buf, '\n');
if (nl)
*nl = '\0';
- flog_err(EC_LIB_VTY, "ERROR: %s on config line %u: %s",
+ flog_err(EC_LIB_VTY, "%s on config line %u: %s",
message, ve->line_num, ve->error_buf);
}
}
@@ -2444,9 +2440,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file,
confp = vty_use_backup_config(fullpath);
if (confp)
- flog_warn(
- EC_LIB_BACKUP_CONFIG,
- "WARNING: using backup configuration file!");
+ flog_warn(EC_LIB_BACKUP_CONFIG,
+ "using backup configuration file!");
else {
flog_err(
EC_LIB_VTY,
@@ -2495,9 +2490,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file,
confp = vty_use_backup_config(config_default_dir);
if (confp) {
- flog_warn(
- EC_LIB_BACKUP_CONFIG,
- "WARNING: using backup configuration file!");
+ flog_warn(EC_LIB_BACKUP_CONFIG,
+ "using backup configuration file!");
fullpath = config_default_dir;
} else {
flog_err(EC_LIB_VTY,
@@ -2625,7 +2619,7 @@ int vty_config_node_exit(struct vty *vty)
/* Check if there's a pending confirmed commit. */
if (vty->t_confirmed_commit_timeout) {
vty_out(vty,
- "WARNING: exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
+ "exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
nb_cli_confirmed_commit_rollback(vty);
nb_cli_confirmed_commit_clean(vty);
}
diff --git a/lib/zclient.c b/lib/zclient.c
index 6ce2113b91..c5e844933c 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -2340,7 +2340,7 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
* "peer" */
flog_err(
EC_LIB_ZAPI_ENCODE,
- "warning: interface %s address %pFX with peer flag set, but no peer address!",
+ "interface %s address %pFX with peer flag set, but no peer address!",
ifp->name, ifc->address);
UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
}
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
index f86dbe3d29..b348cc0def 100644
--- a/nhrpd/nhrp_interface.c
+++ b/nhrpd/nhrp_interface.c
@@ -464,16 +464,22 @@ void nhrp_interface_set_protection(struct interface *ifp, const char *profile,
{
struct nhrp_interface *nifp = ifp->info;
- if (nifp->ipsec_profile)
+ if (nifp->ipsec_profile) {
+ vici_terminate_vc_by_profile_name(nifp->ipsec_profile);
+ nhrp_vc_reset();
free(nifp->ipsec_profile);
+ }
nifp->ipsec_profile = profile ? strdup(profile) : NULL;
- if (nifp->ipsec_fallback_profile)
+ if (nifp->ipsec_fallback_profile) {
+ vici_terminate_vc_by_profile_name(nifp->ipsec_fallback_profile);
+ nhrp_vc_reset();
free(nifp->ipsec_fallback_profile);
+ }
nifp->ipsec_fallback_profile =
fallback_profile ? strdup(fallback_profile) : NULL;
- notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_IPSEC_CHANGED);
}
void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c
index 9ed03098ac..de1bdbd16a 100644
--- a/nhrpd/nhrp_nhs.c
+++ b/nhrpd/nhrp_nhs.c
@@ -116,8 +116,20 @@ static int nhrp_reg_timeout(struct thread *t)
}
r->timeout <<= 1;
- if (r->timeout > 64)
+ if (r->timeout > 64) {
+ /* If registration fails repeatedly, this may be because the
+ * IPSec connection is not working. Close the connection so it
+ * can be re-established correctly
+ */
+ if (r->peer && r->peer->vc && r->peer->vc->ike_uniqueid) {
+ debugf(NHRP_DEBUG_COMMON,
+ "Terminating IPSec Connection for %d\n",
+ r->peer->vc->ike_uniqueid);
+ vici_terminate_vc_by_ike_id(r->peer->vc->ike_uniqueid);
+ r->peer->vc->ike_uniqueid = 0;
+ }
r->timeout = 2;
+ }
thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register);
return 0;
diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
index 622cb4ebb6..0d589e3056 100644
--- a/nhrpd/nhrp_peer.c
+++ b/nhrpd/nhrp_peer.c
@@ -147,6 +147,10 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
case NOTIFY_INTERFACE_ADDRESS_CHANGED:
notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
break;
+ case NOTIFY_INTERFACE_IPSEC_CHANGED:
+ __nhrp_peer_check(p);
+ notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
+ break;
case NOTIFY_INTERFACE_MTU_CHANGED:
notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
break;
@@ -679,7 +683,7 @@ void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type,
zb = zbuf_alloc(1500);
hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma,
&if_ad->addr, &dst);
- hdr->hop_count = 0;
+ hdr->hop_count = 1;
/* Payload is the packet causing indication */
zbuf_copy(zb, pkt, zbuf_used(pkt));
diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
index a36d0c445d..3655463152 100644
--- a/nhrpd/nhrpd.h
+++ b/nhrpd/nhrpd.h
@@ -105,6 +105,7 @@ enum nhrp_notify_type {
NOTIFY_INTERFACE_ADDRESS_CHANGED,
NOTIFY_INTERFACE_NBMA_CHANGED,
NOTIFY_INTERFACE_MTU_CHANGED,
+ NOTIFY_INTERFACE_IPSEC_CHANGED,
NOTIFY_VC_IPSEC_CHANGED,
NOTIFY_VC_IPSEC_UPDATE_NBMA,
@@ -125,6 +126,7 @@ enum nhrp_notify_type {
struct nhrp_vc {
struct notifier_list notifier_list;
uint32_t ipsec;
+ uint32_t ike_uniqueid;
uint8_t updating;
uint8_t abort_migration;
@@ -399,6 +401,8 @@ void nhrp_vc_reset(void);
void vici_init(void);
void vici_terminate(void);
+void vici_terminate_vc_by_profile_name(char *profile_name);
+void vici_terminate_vc_by_ike_id(unsigned int ike_id);
void vici_request_vc(const char *profile, union sockunion *src,
union sockunion *dst, int prio);
diff --git a/nhrpd/vici.c b/nhrpd/vici.c
index 86554f53dc..9b117ddf0d 100644
--- a/nhrpd/vici.c
+++ b/nhrpd/vici.c
@@ -200,6 +200,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx,
nhrp_vc_ipsec_updown(
sactx->child_uniqueid,
vc);
+ vc->ike_uniqueid = sactx->ike_uniqueid;
}
} else {
nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
@@ -521,6 +522,26 @@ void vici_terminate(void)
{
}
+void vici_terminate_vc_by_profile_name(char *profile_name)
+{
+ struct vici_conn *vici = &vici_connection;
+
+ debugf(NHRP_DEBUG_VICI, "Terminate profile = %s", profile_name);
+ vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike",
+ strlen(profile_name), profile_name, VICI_END);
+}
+
+void vici_terminate_vc_by_ike_id(unsigned int ike_id)
+{
+ struct vici_conn *vici = &vici_connection;
+ char ike_id_str[10];
+
+ snprintf(ike_id_str, sizeof(ike_id_str), "%d", ike_id);
+ debugf(NHRP_DEBUG_VICI, "Terminate ike_id_str = %s", ike_id_str);
+ vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id",
+ strlen(ike_id_str), ike_id_str, VICI_END);
+}
+
void vici_request_vc(const char *profile, union sockunion *src,
union sockunion *dst, int prio)
{
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index beca6b9690..977d810be5 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -1106,7 +1106,7 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa)
struct listnode *node, *nnode;
struct ospf6_area *area;
struct ospf6 *ospf6 = oa->ospf6;
-
+ const struct route_node *iterend;
/* skip if router is in other non-stub areas */
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area))
@@ -1114,9 +1114,12 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa)
return;
/* if router is only in a stub area then purge AS-External LSAs */
- for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) {
+ iterend = ospf6_lsdb_head(ospf6->lsdb, 0, 0, 0, &lsa);
+ while (lsa != NULL) {
+ lsanext = ospf6_lsdb_next(iterend, lsa);
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL)
ospf6_lsdb_remove(lsa, ospf6->lsdb);
+ lsa = lsanext;
}
}
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 349dc50b8c..aebe43b9ec 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -86,13 +86,9 @@ const uint16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = {
static void ospf6_header_print(struct ospf6_header *oh)
{
- char router_id[16], area_id[16];
- inet_ntop(AF_INET, &oh->router_id, router_id, sizeof(router_id));
- inet_ntop(AF_INET, &oh->area_id, area_id, sizeof(area_id));
-
- zlog_debug(" OSPFv%d Type:%d Len:%hu Router-ID:%s", oh->version,
- oh->type, ntohs(oh->length), router_id);
- zlog_debug(" Area-ID:%s Cksum:%hx Instance-ID:%d", area_id,
+ zlog_debug(" OSPFv%d Type:%d Len:%hu Router-ID:%pI4", oh->version,
+ oh->type, ntohs(oh->length), &oh->router_id);
+ zlog_debug(" Area-ID:%pI4 Cksum:%hx Instance-ID:%d", &oh->area_id,
ntohs(oh->checksum), oh->instance_id);
}
@@ -100,7 +96,6 @@ void ospf6_hello_print(struct ospf6_header *oh)
{
struct ospf6_hello *hello;
char options[16];
- char drouter[16], bdrouter[16], neighbor[16];
char *p;
ospf6_header_print(oh);
@@ -109,8 +104,6 @@ void ospf6_hello_print(struct ospf6_header *oh)
hello = (struct ospf6_hello *)((caddr_t)oh
+ sizeof(struct ospf6_header));
- inet_ntop(AF_INET, &hello->drouter, drouter, sizeof(drouter));
- inet_ntop(AF_INET, &hello->bdrouter, bdrouter, sizeof(bdrouter));
ospf6_options_printbuf(hello->options, options, sizeof(options));
zlog_debug(" I/F-Id:%ld Priority:%d Option:%s",
@@ -118,14 +111,12 @@ void ospf6_hello_print(struct ospf6_header *oh)
options);
zlog_debug(" HelloInterval:%hu DeadInterval:%hu",
ntohs(hello->hello_interval), ntohs(hello->dead_interval));
- zlog_debug(" DR:%s BDR:%s", drouter, bdrouter);
+ zlog_debug(" DR:%pI4 BDR:%pI4", &hello->drouter, &hello->bdrouter);
for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello));
p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh);
- p += sizeof(uint32_t)) {
- inet_ntop(AF_INET, (void *)p, neighbor, sizeof(neighbor));
- zlog_debug(" Neighbor: %s", neighbor);
- }
+ p += sizeof(uint32_t))
+ zlog_debug(" Neighbor: %pI4", (in_addr_t *)p);
assert(p == OSPF6_MESSAGE_END(oh));
}
@@ -162,7 +153,6 @@ void ospf6_dbdesc_print(struct ospf6_header *oh)
void ospf6_lsreq_print(struct ospf6_header *oh)
{
- char id[16], adv_router[16];
char *p;
ospf6_header_print(oh);
@@ -172,11 +162,9 @@ void ospf6_lsreq_print(struct ospf6_header *oh)
p + sizeof(struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END(oh);
p += sizeof(struct ospf6_lsreq_entry)) {
struct ospf6_lsreq_entry *e = (struct ospf6_lsreq_entry *)p;
- inet_ntop(AF_INET, &e->adv_router, adv_router,
- sizeof(adv_router));
- inet_ntop(AF_INET, &e->id, id, sizeof(id));
- zlog_debug(" [%s Id:%s Adv:%s]", ospf6_lstype_name(e->type),
- id, adv_router);
+
+ zlog_debug(" [%s Id:%pI4 Adv:%pI4]",
+ ospf6_lstype_name(e->type), &e->id, &e->adv_router);
}
assert(p == OSPF6_MESSAGE_END(oh));
@@ -856,15 +844,11 @@ static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst,
/* Find database copy */
lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb);
if (lsa == NULL) {
- char id[16], adv_router[16];
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) {
- inet_ntop(AF_INET, &e->id, id, sizeof(id));
- inet_ntop(AF_INET, &e->adv_router, adv_router,
- sizeof(adv_router));
zlog_debug(
- "Can't find requested [%s Id:%s Adv:%s]",
- ospf6_lstype_name(e->type), id,
- adv_router);
+ "Can't find requested [%s Id:%pI4 Adv:%pI4]",
+ ospf6_lstype_name(e->type), &e->id,
+ &e->adv_router);
}
thread_add_event(master, bad_lsreq, on, 0, NULL);
return;
@@ -1321,7 +1305,6 @@ static int ospf6_rxpacket_examin(struct ospf6_interface *oi,
struct ospf6_header *oh,
const unsigned bytesonwire)
{
- char buf[2][INET_ADDRSTRLEN];
if (MSG_OK != ospf6_packet_examin(oh, bytesonwire))
return MSG_NG;
@@ -1335,13 +1318,10 @@ static int ospf6_rxpacket_examin(struct ospf6_interface *oi,
oi->interface->name);
else
zlog_warn(
- "VRF %s: I/F %s Area-ID mismatch (my %s, rcvd %s)",
+ "VRF %s: I/F %s Area-ID mismatch (my %pI4, rcvd %pI4)",
vrf_id_to_name(oi->interface->vrf_id),
- oi->interface->name,
- inet_ntop(AF_INET, &oi->area->area_id, buf[0],
- INET_ADDRSTRLEN),
- inet_ntop(AF_INET, &oh->area_id, buf[1],
- INET_ADDRSTRLEN));
+ oi->interface->name, &oi->area->area_id,
+ &oh->area_id);
return MSG_NG;
}
@@ -1356,11 +1336,9 @@ static int ospf6_rxpacket_examin(struct ospf6_interface *oi,
/* Router-ID check */
if (oh->router_id == oi->area->ospf6->router_id) {
- zlog_warn("VRF %s: I/F %s Duplicate Router-ID (%s)",
+ zlog_warn("VRF %s: I/F %s Duplicate Router-ID (%pI4)",
vrf_id_to_name(oi->interface->vrf_id),
- oi->interface->name,
- inet_ntop(AF_INET, &oh->router_id, buf[0],
- INET_ADDRSTRLEN));
+ oi->interface->name, &oh->router_id);
return MSG_NG;
}
return MSG_OK;
@@ -1541,7 +1519,6 @@ int ospf6_receive(struct thread *thread)
{
int sockfd;
unsigned int len;
- char srcname[64], dstname[64];
struct in6_addr src, dst;
ifindex_t ifindex;
struct iovec iovector[2];
@@ -1598,13 +1575,11 @@ int ospf6_receive(struct thread *thread)
/* Log */
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) {
- inet_ntop(AF_INET6, &src, srcname, sizeof(srcname));
- inet_ntop(AF_INET6, &dst, dstname, sizeof(dstname));
zlog_debug("%s received on %s",
lookup_msg(ospf6_message_type_str, oh->type, NULL),
oi->interface->name);
- zlog_debug(" src: %s", srcname);
- zlog_debug(" dst: %s", dstname);
+ zlog_debug(" src: %pI6", &src);
+ zlog_debug(" dst: %pI6", &dst);
switch (oh->type) {
case OSPF6_MESSAGE_TYPE_HELLO:
@@ -1659,7 +1634,7 @@ static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
struct ospf6_interface *oi, struct ospf6_header *oh)
{
unsigned int len;
- char srcname[64], dstname[64];
+ char srcname[64];
struct iovec iovector[2];
/* initialize */
@@ -1680,7 +1655,6 @@ static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
/* Log */
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) {
- inet_ntop(AF_INET6, dst, dstname, sizeof(dstname));
if (src)
inet_ntop(AF_INET6, src, srcname, sizeof(srcname));
else
@@ -1689,7 +1663,7 @@ static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
lookup_msg(ospf6_message_type_str, oh->type, NULL),
oi->interface->name);
zlog_debug(" src: %s", srcname);
- zlog_debug(" dst: %s", dstname);
+ zlog_debug(" dst: %pI6", dst);
switch (oh->type) {
case OSPF6_MESSAGE_TYPE_HELLO:
@@ -1855,7 +1829,9 @@ int ospf6_dbdesc_send(struct thread *thread)
/* MTU check */
if (p - sendbuf + sizeof(struct ospf6_lsa_header)
> ospf6_packet_max(on->ospf6_if)) {
- ospf6_lsdb_lsa_unlock(lsa);
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
break;
}
memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
@@ -1902,7 +1878,9 @@ int ospf6_dbdesc_send_newone(struct thread *thread)
if (size + sizeof(struct ospf6_lsa_header)
> ospf6_packet_max(on->ospf6_if)) {
- ospf6_lsdb_lsa_unlock(lsa);
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
break;
}
@@ -1961,7 +1939,9 @@ int ospf6_lsreq_send(struct thread *thread)
/* MTU check */
if (p - sendbuf + sizeof(struct ospf6_lsreq_entry)
> ospf6_packet_max(on->ospf6_if)) {
- ospf6_lsdb_lsa_unlock(lsa);
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
break;
}
@@ -2422,7 +2402,9 @@ int ospf6_lsack_send_interface(struct thread *thread)
thread_add_event(master, ospf6_lsack_send_interface, oi,
0, &oi->thread_send_lsack);
- ospf6_lsdb_lsa_unlock(lsa);
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
break;
}
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 2b7072d34f..9b9453ce24 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -96,13 +96,9 @@ static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS)
return 0;
o->router_id_zebra = router_id.u.prefix4;
- if (IS_OSPF6_DEBUG_ZEBRA(RECV)) {
- char buf[INET_ADDRSTRLEN];
-
- zlog_debug("%s: zebra router-id %s update", __func__,
- inet_ntop(AF_INET, &router_id.u.prefix4, buf,
- INET_ADDRSTRLEN));
- }
+ if (IS_OSPF6_DEBUG_ZEBRA(RECV))
+ zlog_debug("%s: zebra router-id %pI4 update", __func__,
+ &router_id.u.prefix4);
ospf6_router_id_update(o);
diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c
index 2f9ff4f0f0..8b5ca8aff3 100644
--- a/pathd/path_pcep.c
+++ b/pathd/path_pcep.c
@@ -17,7 +17,7 @@
*/
#include <zebra.h>
-#include <pcep_utils_counters.h>
+#include "pceplib/pcep_utils_counters.h"
#include "log.h"
#include "command.h"
@@ -215,29 +215,10 @@ int pcep_main_event_update_candidate(struct path *path)
ret = path_pcep_config_update_path(path);
if (ret != PATH_NB_ERR && path->srp_id != 0) {
- /* ODL and Cisco requires the first reported
- * LSP to have a DOWN status, the later status changes
- * will be comunicated through hook calls.
- */
- enum pcep_lsp_operational_status real_status;
if ((resp = path_pcep_config_get_path(&path->nbkey))) {
resp->srp_id = path->srp_id;
- real_status = resp->status;
- resp->status = PCEP_LSP_OPERATIONAL_DOWN;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp);
- /* If the update did not have any effect and the real
- * status is not DOWN, we need to send a second report
- * so the PCE is aware of the real status. This is due
- * to the fact that NO notification will be received
- * if the update did not apply any changes */
- if ((ret == PATH_NB_NO_CHANGE)
- && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
- resp->status = real_status;
- resp->srp_id = 0;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id,
- resp);
- }
- pcep_free_path(resp);
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
+ ret == PATH_NB_NO_CHANGE);
}
}
return ret;
diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h
index 1896c265c1..b131b31445 100644
--- a/pathd/path_pcep.h
+++ b/pathd/path_pcep.h
@@ -22,8 +22,8 @@
#include <stdbool.h>
#include <debug.h>
#include <netinet/tcp.h>
-#include <pcep_utils_logging.h>
-#include <pcep_pcc_api.h>
+#include "pceplib/pcep_utils_logging.h"
+#include "pceplib/pcep_pcc_api.h"
#include "mpls.h"
#include "pathd/pathd.h"
#include "pathd/path_pcep_memory.h"
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
index add3391f22..7bbed9464d 100644
--- a/pathd/path_pcep_cli.c
+++ b/pathd/path_pcep_cli.c
@@ -18,8 +18,8 @@
*/
#include <zebra.h>
-#include <pcep_utils_counters.h>
-#include <pcep_session_logic.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_session_logic.h"
#include "log.h"
#include "command.h"
@@ -321,8 +321,9 @@ pcep_cli_merge_pcep_pce_config_options(struct pce_opts_cli *pce_opts_cli)
default_pcep_config_group_opts_g.tcp_md5_auth;
}
}
- strncpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
- tcp_md5_auth_str, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
+ tcp_md5_auth_str,
+ sizeof(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth));
struct ipaddr *source_ip =
&pce_opts_cli->pce_config_group_opts.source_ip;
@@ -525,8 +526,9 @@ static int path_pcep_cli_show_srte_pcep_counters(struct vty *vty)
tm_info = localtime(&group->start_time);
strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info);
- vty_out(vty, "PCEP counters since %s (%luh %lum %lus):\n", tm_buffer,
- diff_time / 3600, (diff_time / 60) % 60, diff_time % 60);
+ vty_out(vty, "PCEP counters since %s (%uh %um %us):\n", tm_buffer,
+ (uint32_t)(diff_time / 3600), (uint32_t)((diff_time / 60) % 60),
+ (uint32_t)(diff_time % 60));
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
@@ -815,7 +817,8 @@ static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty,
return CMD_ERR_NO_MATCH;
}
- strncpy(pce_config->tcp_md5_auth, tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(pce_config->tcp_md5_auth, tcp_md5_auth,
+ sizeof(pce_config->tcp_md5_auth));
return CMD_SUCCESS;
}
@@ -1220,8 +1223,9 @@ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts,
localtime_r(&current_time, &lt);
gmtime_r(&session->time_connected, &lt);
vty_out(vty,
- " Connected for %ld seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
- (current_time - session->time_connected),
+ " Connected for %u seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
+ (uint32_t)(current_time
+ - session->time_connected),
lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
lt.tm_hour, lt.tm_min, lt.tm_sec);
}
diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c
index 989223ebc3..107475bec9 100644
--- a/pathd/path_pcep_config.c
+++ b/pathd/path_pcep_config.c
@@ -19,7 +19,7 @@
#include <northbound.h>
#include <yang.h>
#include <printfrr.h>
-#include <pcep-objects.h>
+#include "pceplib/pcep_msg_objects.h"
#include "pathd/pathd.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_config.h"
@@ -45,14 +45,13 @@ status_int_to_ext(enum srte_policy_status status);
static enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type);
static enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type);
-static int path_pcep_config_lookup_cb(struct thread *t)
+void path_pcep_refine_path(struct path *path)
{
- struct path *path = THREAD_ARG(t);
struct srte_candidate *candidate = lookup_candidate(&path->nbkey);
struct srte_lsp *lsp;
if (candidate == NULL)
- return 0;
+ return;
lsp = candidate->lsp;
@@ -65,16 +64,6 @@ static int path_pcep_config_lookup_cb(struct thread *t)
if ((path->update_origin == SRTE_ORIGIN_UNDEFINED)
&& (lsp->segment_list != NULL))
path->update_origin = lsp->segment_list->protocol_origin;
-
- return 0;
-}
-
-void path_pcep_config_lookup(struct path *path)
-{
- /*
- * Configuration access is strictly done via the main thread
- */
- thread_execute(master, path_pcep_config_lookup_cb, path, 0);
}
struct path *path_pcep_config_get_path(struct lsp_nb_key *key)
@@ -346,9 +335,11 @@ int path_pcep_config_update_path(struct path *path)
SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
for (metric = path->first_metric; metric != NULL; metric = metric->next)
- srte_lsp_set_metric(candidate->lsp, metric->type, metric->value,
- metric->enforce, metric->is_bound,
- metric->is_computed);
+ srte_lsp_set_metric(
+ candidate->lsp,
+ (enum srte_candidate_metric_type)metric->type,
+ metric->value, metric->enforce, metric->is_bound,
+ metric->is_computed);
if (path->has_bandwidth)
srte_lsp_set_bandwidth(candidate->lsp, path->bandwidth,
diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h
index de29ab29c1..223dd10c82 100644
--- a/pathd/path_pcep_config.h
+++ b/pathd/path_pcep_config.h
@@ -31,10 +31,11 @@
typedef int (*path_list_cb_t)(struct path *path, void *arg);
/* Lookup the candidate path and fill up the missing path attributes like name
- and type. Used for path generated from PCEP message received from the PCE
- so they contains more information about the candidate path. If no matching
- policy or candidate path is found, nothing is changed */
-void path_pcep_config_lookup(struct path *path);
+ * and type. Used for path generated from PCEP message received from the PCE
+ * so they contains more information about the candidate path. If no matching
+ * policy or candidate path is found, nothing is changed.
+ * MUST BE CALLED FROM THE MAIN THREAD */
+void path_pcep_refine_path(struct path *path);
struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
int path_pcep_config_update_path(struct path *path);
diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c
index f4871a4d8d..db7d2b55a5 100644
--- a/pathd/path_pcep_controller.c
+++ b/pathd/path_pcep_controller.c
@@ -55,7 +55,9 @@ enum pcep_ctrl_event_type {
EV_SYNC_PATH,
EV_SYNC_DONE,
EV_PCEPLIB_EVENT,
- EV_RESET_PCC_SESSION
+ EV_RESET_PCC_SESSION,
+ EV_SEND_REPORT,
+ EV_PATH_REFINED
};
struct pcep_ctrl_event_data {
@@ -73,18 +75,20 @@ struct pcep_main_event_data {
void *payload;
};
-/* Synchronous call arguments */
-
-struct get_counters_args {
+struct pcep_refine_path_event_data {
struct ctrl_state *ctrl_state;
int pcc_id;
- struct counters_group *counters;
+ pcep_refine_callback_t continue_lsp_update_handler;
+ struct path *path;
+ void *payload;
};
-struct send_report_args {
+/* Synchronous call arguments */
+
+struct get_counters_args {
struct ctrl_state *ctrl_state;
int pcc_id;
- struct path *path;
+ struct counters_group *counters;
};
struct get_pcep_session_args {
@@ -95,13 +99,10 @@ struct get_pcep_session_args {
/* Internal Functions Called From Main Thread */
static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res);
+static int pcep_refine_path_event_cb(struct thread *thread);
/* Internal Functions Called From Controller Thread */
static int pcep_thread_finish_event_handler(struct thread *thread);
-static int pcep_thread_get_counters_callback(struct thread *t);
-static int pcep_thread_send_report_callback(struct thread *t);
-static int pcep_thread_get_pcep_session_callback(struct thread *t);
-static int pcep_thread_get_pcc_info_callback(struct thread *t);
/* Controller Thread Timer Handler */
static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
@@ -148,6 +149,9 @@ static int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state,
static int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state,
enum pcep_pathd_event_type type,
struct path *path);
+static void
+pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data);
/* Main Thread Event Handler */
static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id,
@@ -280,48 +284,50 @@ struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt,
int pcc_id)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct get_counters_args args = {
- .ctrl_state = ctrl_state, .pcc_id = pcc_id, .counters = NULL};
- thread_execute(ctrl_state->self, pcep_thread_get_counters_callback,
- &args, 0);
- return args.counters;
+ struct counters_group *counters = NULL;
+ struct pcc_state *pcc_state;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ counters = pcep_lib_copy_counters(pcc_state->sess);
+ }
+ return counters;
}
pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct get_pcep_session_args args = {.ctrl_state = ctrl_state,
- .pcc_id = pcc_id,
- .pcep_session = NULL};
- thread_execute(ctrl_state->self, pcep_thread_get_pcep_session_callback,
- &args, 0);
- return args.pcep_session;
+ struct pcc_state *pcc_state;
+ pcep_session *session = NULL;
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ session = pcep_lib_copy_pcep_session(pcc_state->sess);
+ }
+ return session;
}
struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
const char *pce_name)
{
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct pcep_pcc_info *args = XCALLOC(MTYPE_PCEP, sizeof(*args));
- args->ctrl_state = ctrl_state;
- strncpy(args->pce_name, pce_name, sizeof(args->pce_name));
- thread_execute(ctrl_state->self, pcep_thread_get_pcc_info_callback,
- args, 0);
+ struct pcep_pcc_info *pcc_info = XCALLOC(MTYPE_PCEP, sizeof(*pcc_info));
+ if( pcc_info && ctrl_state){
+ strlcpy(pcc_info->pce_name, pce_name, sizeof(pcc_info->pce_name));
+ pcep_pcc_copy_pcc_info(ctrl_state->pcc, pcc_info);
+ }
- return args;
+ return pcc_info;
}
-void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
- struct path *path)
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable)
{
- /* Sends a report stynchronously */
struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
- struct send_report_args args = {
- .ctrl_state = ctrl_state, .pcc_id = pcc_id, .path = path};
- thread_execute(ctrl_state->self, pcep_thread_send_report_callback,
- &args, 0);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_REPORT, is_stable,
+ path);
}
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
@@ -333,6 +339,20 @@ int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
return 0;
}
+int pcep_refine_path_event_cb(struct thread *thread)
+{
+ struct pcep_refine_path_event_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ struct path *path = data->path;
+ assert(path != NULL);
+ int pcc_id = data->pcc_id;
+
+
+ path_pcep_refine_path(path);
+ return send_to_thread(ctrl_state, pcc_id, EV_PATH_REFINED, 0, data);
+}
+
/* ------------ API Functions Called From Controller Thread ------------ */
@@ -442,6 +462,41 @@ int pcep_thread_pcc_count(struct ctrl_state *ctrl_state)
return ctrl_state->pcc_count;
}
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload)
+{
+ struct pcep_refine_path_event_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->path = path;
+ data->pcc_id = pcc_id;
+ data->continue_lsp_update_handler = cb;
+ data->payload = payload;
+
+ thread_add_event(ctrl_state->main, pcep_refine_path_event_cb,
+ (void *)data, 0, NULL);
+ return 0;
+}
+
+void pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data)
+{
+ assert(data != NULL);
+ int pcc_id = data->pcc_id;
+ pcep_refine_callback_t continue_lsp_update_handler = data->continue_lsp_update_handler;
+ assert(continue_lsp_update_handler != NULL);
+ struct path *path = data->path;
+ void *payload = data->payload;
+ struct pcc_state *pcc_state = NULL;
+ XFREE(MTYPE_PCEP, data);
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ continue_lsp_update_handler(ctrl_state, pcc_state, path, payload);
+}
+
+
/* ------------ Internal Functions Called From Controller Thread ------------ */
int pcep_thread_finish_event_handler(struct thread *thread)
@@ -467,78 +522,6 @@ int pcep_thread_finish_event_handler(struct thread *thread)
return 0;
}
-int pcep_thread_get_counters_callback(struct thread *t)
-{
- struct get_counters_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- if (pcc_state) {
- args->counters = pcep_lib_copy_counters(pcc_state->sess);
- } else {
- args->counters = NULL;
- }
-
- return 0;
-}
-
-int pcep_thread_send_report_callback(struct thread *t)
-{
- struct send_report_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- if (args->pcc_id == 0) {
- for (int i = 0; i < MAX_PCC; i++) {
- if (ctrl_state->pcc[i]) {
- pcep_pcc_send_report(ctrl_state,
- ctrl_state->pcc[i],
- args->path);
- }
- }
- } else {
- pcc_state =
- pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- pcep_pcc_send_report(ctrl_state, pcc_state, args->path);
- }
-
- return 0;
-}
-
-int pcep_thread_get_pcep_session_callback(struct thread *t)
-{
- struct get_pcep_session_args *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
- struct pcc_state *pcc_state;
-
- pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
- if (pcc_state) {
- args->pcep_session =
- pcep_lib_copy_pcep_session(pcc_state->sess);
- }
-
- return 0;
-}
-
-int pcep_thread_get_pcc_info_callback(struct thread *t)
-{
- struct pcep_pcc_info *args = THREAD_ARG(t);
- assert(args != NULL);
- struct ctrl_state *ctrl_state = args->ctrl_state;
- assert(ctrl_state != NULL);
-
- pcep_pcc_copy_pcc_info(ctrl_state->pcc, args);
-
- return 0;
-}
-
/* ------------ Controller Thread Timer Handler ------------ */
int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id,
@@ -752,11 +735,14 @@ int pcep_thread_event_handler(struct thread *thread)
/* Possible sub-type values */
enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED;
- /* Possible payload values */
+ /* Possible payload values, maybe an union would be better... */
struct path *path = NULL;
struct pcc_opts *pcc_opts = NULL;
struct pce_opts *pce_opts = NULL;
struct pcc_state *pcc_state = NULL;
+ struct pcep_refine_path_event_data *refine_data = NULL;
+
+ struct path *path_copy = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
@@ -808,6 +794,30 @@ int pcep_thread_event_handler(struct thread *thread)
(const char *)payload);
}
break;
+ case EV_SEND_REPORT:
+ assert(payload != NULL);
+ path = (struct path *)payload;
+ if (pcc_id == 0) {
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ path_copy = pcep_copy_path(path);
+ pcep_pcc_send_report(
+ ctrl_state, ctrl_state->pcc[i],
+ path_copy, (bool)sub_type);
+ }
+ }
+ } else {
+ pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_report(ctrl_state, pcc_state, path,
+ (bool)sub_type);
+ }
+ break;
+ case EV_PATH_REFINED:
+ assert(payload != NULL);
+ refine_data = (struct pcep_refine_path_event_data *)payload;
+ pcep_thread_path_refined_event(ctrl_state, refine_data);
+ break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
@@ -984,6 +994,7 @@ int pcep_main_event_handler(struct thread *thread)
/* ------------ Helper functions ------------ */
+
void set_ctrl_state(struct frr_pthread *fpt, struct ctrl_state *ctrl_state)
{
assert(fpt != NULL);
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
index f6eaa0ca2a..1b7c3a4c72 100644
--- a/pathd/path_pcep_controller.h
+++ b/pathd/path_pcep_controller.h
@@ -21,16 +21,21 @@
#include "pathd/path_pcep.h"
+struct ctrl_state;
+struct pcc_state;
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
- PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP
+ PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP,
};
typedef int (*pcep_main_event_handler_t)(enum pcep_main_event_type type,
int pcc_id, void *payload);
+typedef void (*pcep_refine_callback_t)(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct path *path, void *payload);
enum pcep_pathd_event_type {
PCEP_PATH_UNDEFINED = 0,
@@ -124,10 +129,13 @@ pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id);
struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
const char *pce_name);
-/* Synchronously send a report, the caller is responsible to free the path,
- * If `pcc_id` is `0` the report is sent by all PCCs */
-void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
- struct path *path);
+/* Asynchronously send a report. The caller is giving away the path structure,
+ * it shouldn't be allocated on the stack. If `pcc_id` is `0` the report is
+ * sent by all PCCs. The parameter is_stable is used to hint wether the status
+ * will soon change, this is used to ensure all report updates are sent even
+ * when missing status update events */
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable);
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
@@ -162,5 +170,9 @@ int pcep_thread_send_ctrl_event(void *fpt, void *payload,
pcep_ctrl_thread_callback cb);
int pcep_thread_pcep_event(struct thread *thread);
int pcep_thread_pcc_count(struct ctrl_state *ctrl_state);
+/* Called by the PCC to refine a path in the main thread */
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload);
#endif // _PATH_PCEP_CONTROLLER_H_
diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c
index bcaadfe4d8..d222371bbb 100644
--- a/pathd/path_pcep_debug.c
+++ b/pathd/path_pcep_debug.c
@@ -636,8 +636,8 @@ const char *pcep_message_type_name(enum pcep_message_types pcep_message_type)
return "UPDATE";
case PCEP_TYPE_INITIATE:
return "INITIATE";
- case PCEP_TYPE_UNKOWN_MSG:
- return "UNKOWN_MSG";
+ case PCEP_TYPE_START_TLS:
+ return "START_TLS";
default:
return "UNKNOWN";
}
@@ -1288,7 +1288,7 @@ void _format_path_hop(int ps, struct path_hop *hop)
&hop->nai.remote_addr.ipaddr_v6);
break;
case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
- PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "",
+ PATHD_FORMAT("%*sNAI: %pI6(%u)/%pI6(%u)\n", ps, "",
&hop->nai.local_addr.ipaddr_v6,
hop->nai.local_iface,
&hop->nai.remote_addr.ipaddr_v6,
diff --git a/pathd/path_pcep_debug.h b/pathd/path_pcep_debug.h
index 68b29ab657..5a504e4e1a 100644
--- a/pathd/path_pcep_debug.h
+++ b/pathd/path_pcep_debug.h
@@ -20,8 +20,8 @@
#define _PATH_PCEP_DEBUG_H_
#include "pathd/path_debug.h"
-#include <pcep_pcc_api.h>
-#include <pcep-objects.h>
+#include "pceplib/pcep_pcc_api.h"
+#include "pceplib/pcep_msg_objects.h"
#include "pathd/path_pcep.h"
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_pcc.h"
diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c
index bb6bfb1336..1d2f25889e 100644
--- a/pathd/path_pcep_lib.c
+++ b/pathd/path_pcep_lib.c
@@ -16,9 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <zebra.h>
+
#include <debug.h>
-#include <pcep_utils_counters.h>
-#include <pcep_timers.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_timers.h"
#include "pathd/path_errors.h"
#include "pathd/path_memory.h"
#include "pathd/path_pcep.h"
@@ -176,11 +178,11 @@ pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr,
/* TODO when available in the pceplib, set it here
pcep_options->state_timeout_inteval_seconds;*/
- if (pcep_options->tcp_md5_auth != NULL
- && pcep_options->tcp_md5_auth[0] != '\0') {
+ if (pcep_options->tcp_md5_auth[0] != '\0') {
config->is_tcp_auth_md5 = true;
- strncpy(config->tcp_authentication_str,
- pcep_options->tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN);
+ strlcpy(config->tcp_authentication_str,
+ pcep_options->tcp_md5_auth,
+ sizeof(config->tcp_authentication_str));
} else {
config->is_tcp_auth_md5 = false;
}
diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h
index 3bea28432d..3f34edcb3f 100644
--- a/pathd/path_pcep_lib.h
+++ b/pathd/path_pcep_lib.h
@@ -20,7 +20,7 @@
#define _PATH_PCEP_LIB_H_
#include <stdbool.h>
-#include <pcep_pcc_api.h>
+#include "pceplib/pcep_pcc_api.h"
#include "frr_pthread.h"
#include "pathd/path_pcep.h"
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
index c1f60edd22..899ce805fe 100644
--- a/pathd/path_pcep_pcc.c
+++ b/pathd/path_pcep_pcc.c
@@ -55,6 +55,7 @@
#define MAX_ERROR_MSG_SIZE 256
#define MAX_COMPREQ_TRIES 3
+pthread_mutex_t g_pcc_info_mtx = PTHREAD_MUTEX_INITIALIZER;
/* PCEP Event Handler */
static void handle_pcep_open(struct ctrl_state *ctrl_state,
@@ -63,12 +64,15 @@ static void handle_pcep_open(struct ctrl_state *ctrl_state,
static void handle_pcep_message(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
+static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
static void handle_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
-static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+static void continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
- struct pcep_message *msg);
+ struct path *path, void *payload);
static void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg);
@@ -344,9 +348,6 @@ void pcep_pcc_reconnect(struct ctrl_state *ctrl_state,
int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
{
- char pcc_buff[40];
- char pce_buff[40];
-
assert(pcc_state->status == PCEP_PCC_DISCONNECTED);
assert(pcc_state->sess == NULL);
@@ -362,17 +363,14 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) {
if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) {
flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
- "skipping connection to PCE %s:%d due to "
- "missing PCC IPv4 address",
- ipaddr2str(&pcc_state->pce_opts->addr,
- pce_buff, sizeof(pce_buff)),
+ "skipping connection to PCE %pIA:%d due to missing PCC IPv4 address",
+ &pcc_state->pce_opts->addr,
pcc_state->pce_opts->port);
schedule_reconnect(ctrl_state, pcc_state);
return 0;
} else {
flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
- "missing IPv4 PCC address, IPv4 candidate "
- "paths will be ignored");
+ "missing IPv4 PCC address, IPv4 candidate paths will be ignored");
}
}
@@ -381,17 +379,14 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) {
flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
- "skipping connection to PCE %s:%d due to "
- "missing PCC IPv6 address",
- ipaddr2str(&pcc_state->pce_opts->addr,
- pce_buff, sizeof(pce_buff)),
+ "skipping connection to PCE %pIA:%d due to missing PCC IPv6 address",
+ &pcc_state->pce_opts->addr,
pcc_state->pce_opts->port);
schedule_reconnect(ctrl_state, pcc_state);
return 0;
} else {
flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
- "missing IPv6 PCC address, IPv6 candidate "
- "paths will be ignored");
+ "missing IPv6 PCC address, IPv6 candidate paths will be ignored");
}
}
@@ -399,10 +394,8 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
* have been spent, we still need the one for the transport familly */
if (pcc_state->pcc_addr_tr.ipa_type == IPADDR_NONE) {
flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
- "skipping connection to PCE %s:%d due to missing "
- "PCC address",
- ipaddr2str(&pcc_state->pce_opts->addr, pce_buff,
- sizeof(pce_buff)),
+ "skipping connection to PCE %pIA:%d due to missing PCC address",
+ &pcc_state->pce_opts->addr,
pcc_state->pce_opts->port);
schedule_reconnect(ctrl_state, pcc_state);
return 0;
@@ -416,12 +409,10 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
if (pcc_state->sess == NULL) {
flog_warn(EC_PATH_PCEP_LIB_CONNECT,
- "failed to connect to PCE %s:%d from %s:%d",
- ipaddr2str(&pcc_state->pce_opts->addr, pce_buff,
- sizeof(pce_buff)),
+ "failed to connect to PCE %pIA:%d from %pIA:%d",
+ &pcc_state->pce_opts->addr,
pcc_state->pce_opts->port,
- ipaddr2str(&pcc_state->pcc_addr_tr, pcc_buff,
- sizeof(pcc_buff)),
+ &pcc_state->pcc_addr_tr,
pcc_state->pcc_opts->port);
schedule_reconnect(ctrl_state, pcc_state);
return 0;
@@ -494,8 +485,7 @@ void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
send_report(pcc_state, path);
} else {
PCEP_DEBUG(
- "%s Skipping %s candidate path %s "
- "synchronization",
+ "%s Skipping %s candidate path %s synchronization",
pcc_state->tag,
ipaddr_type_name(&path->nbkey.endpoint),
path->name);
@@ -543,23 +533,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 +936,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 +981,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 +1106,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 +1172,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));
@@ -1204,8 +1229,7 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
* the connection if more that a given rate.
*/
PCEP_DEBUG(
- "%s Received computation reply for unknown request "
- "%d",
+ "%s Received computation reply for unknown request %d",
pcc_state->tag, path->req_id);
PCEP_DEBUG_PATH("%s", format_path(path));
send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF,
@@ -1309,13 +1333,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;
}
}
}
@@ -1535,7 +1559,6 @@ void send_comp_request(struct ctrl_state *ctrl_state,
assert(lookup_reqid(pcc_state, req->path) == req->path->req_id);
int timeout;
- char buff[40];
struct pcep_message *msg;
if (!pcc_state->is_best) {
@@ -1546,10 +1569,9 @@ void send_comp_request(struct ctrl_state *ctrl_state,
specialize_outgoing_path(pcc_state, req->path);
PCEP_DEBUG(
- "%s Sending computation request %d for path %s to %s (retry %d)",
+ "%s Sending computation request %d for path %s to %pIA (retry %d)",
pcc_state->tag, req->path->req_id, req->path->name,
- ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)),
- req->retry_count);
+ &req->path->nbkey.endpoint, req->retry_count);
PCEP_DEBUG_PATH("%s Computation request path %s: %s", pcc_state->tag,
req->path->name, format_path(req->path));
@@ -1582,7 +1604,6 @@ void cancel_comp_requests(struct ctrl_state *ctrl_state,
void cancel_comp_request(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct req_entry *req)
{
- char buff[40];
struct pcep_message *msg;
if (req->was_sent) {
@@ -1592,10 +1613,9 @@ void cancel_comp_request(struct ctrl_state *ctrl_state,
}
PCEP_DEBUG(
- "%s Canceling computation request %d for path %s to %s (retry %d)",
+ "%s Canceling computation request %d for path %s to %pIA (retry %d)",
pcc_state->tag, req->path->req_id, req->path->name,
- ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)),
- req->retry_count);
+ &req->path->nbkey.endpoint, req->retry_count);
PCEP_DEBUG_PATH("%s Canceled computation request path %s: %s",
pcc_state->tag, req->path->name,
format_path(req->path));
diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h
index a466d92d50..c07a6ae541 100644
--- a/pathd/path_pcep_pcc.h
+++ b/pathd/path_pcep_pcc.h
@@ -113,13 +113,18 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
struct path *path);
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
- enum pcep_ctrl_timer_type type, void *param);
+ enum pcep_ctrl_timeout_type type, void *param);
void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path);
void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state);
+/* Send a report explicitly. When doing so the PCC may send multiple reports
+ * due to expectations from vendors for the first report to be with a DOWN
+ * status. The parameter is_stable is used for that purpose as a hint wheter
+ * to expect an update for the report */
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
- struct pcc_state *pcc_state, struct path *path);
+ struct pcc_state *pcc_state, struct path *path,
+ bool is_stable);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
diff --git a/pathd/pathd.c b/pathd/pathd.c
index e2c7c95728..14f5167bff 100644
--- a/pathd/pathd.c
+++ b/pathd/pathd.c
@@ -624,8 +624,7 @@ void srte_candidate_set_metric(struct srte_candidate *candidate,
char endpoint[46];
ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
zlog_debug(
- "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f "
- "(is-bound: %s; is_computed: %s)",
+ "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f (is-bound: %s; is_computed: %s)",
endpoint, policy->color, candidate->name,
required ? "required " : "", srte_candidate_metric_name(type),
type, value, is_bound ? "true" : "false",
@@ -659,8 +658,7 @@ void srte_lsp_set_metric(struct srte_lsp *lsp,
char endpoint[46];
ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
zlog_debug(
- "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f "
- "(is-bound: %s; is_computed: %s)",
+ "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f (is-bound: %s; is_computed: %s)",
endpoint, policy->color, candidate->name,
required ? "required " : "", srte_candidate_metric_name(type),
type, value, is_bound ? "true" : "false",
@@ -981,8 +979,7 @@ void srte_candidate_unset_segment_list(const char *originator, bool force)
if (segment_list->protocol_origin
== SRTE_ORIGIN_LOCAL) {
zlog_warn(
- "Cannot unset segment list %s because it "
- "was created locally",
+ "Cannot unset segment list %s because it was created locally",
segment_list->name);
continue;
}
diff --git a/pathd/subdir.am b/pathd/subdir.am
index 520a8c696a..452d824669 100644
--- a/pathd/subdir.am
+++ b/pathd/subdir.am
@@ -11,7 +11,7 @@ vtysh_daemons += pathd
# TODO add man page
#man8 += $(MANBUILD)/pathd.8
-if HAVE_PATHD_PCEP
+if PATHD_PCEP
vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c
module_LTLIBRARIES += pathd/pathd_pcep.la
endif
@@ -69,6 +69,15 @@ pathd_pathd_pcep_la_SOURCES = \
pathd/path_pcep_config.c \
pathd/path_pcep_pcc.c \
# end
+
+if PATHD_PCEP
+pathd_pathd_pcep_la_CPPFLAGS = -I./pceplib $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD = pceplib/libpcep_pcc.la
+else
+pathd_pathd_pcep_la_CPPFLAGS = $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD =
+endif
+
+
pathd_pathd_pcep_la_CFLAGS = $(WERROR)
pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
-pathd_pathd_pcep_la_LIBADD = @PATHD_PCEP_LIBS@
diff --git a/pceplib/.gitignore b/pceplib/.gitignore
new file mode 100644
index 0000000000..5861f25a41
--- /dev/null
+++ b/pceplib/.gitignore
@@ -0,0 +1,14 @@
+pcep_pcc
+test/pcep_msg_tests
+test/pcep_pcc_api_tests
+test/pcep_session_logic_tests
+test/pcep_socket_comm_tests
+test/pcep_timers_tests
+test/pcep_utils_tests
+test/valgrind.pcep_msg_tests.log
+test/valgrind.pcep_pcc_api_tests.log
+test/valgrind.pcep_session_logic_tests.log
+test/valgrind.pcep_socket_comm_tests.log
+test/valgrind.pcep_timers_tests.log
+test/valgrind.pcep_utils_tests.log
+../test-driver
diff --git a/pceplib/pcep.h b/pceplib/pcep.h
new file mode 100644
index 0000000000..278ab9d5dc
--- /dev/null
+++ b/pceplib/pcep.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_H_
+#define PCEP_H_
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(linux) || defined(GNU_LINUX)
+//#include <netinet/in.h>
+#define ipv6_u __in6_u
+#else
+// bsd family
+#define TCP_MD5SIG_MAXKEYLEN 80
+//#include <netinet/in.h>
+#define ipv6_u __u6_addr
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#else
+#include <endian.h>
+#endif /* __FreeBSD__ */
+#endif
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#endif
diff --git a/pceplib/pcep_msg_encoding.h b/pceplib/pcep_msg_encoding.h
new file mode 100644
index 0000000000..d835b87f94
--- /dev/null
+++ b/pceplib/pcep_msg_encoding.h
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Definitions for encoding and decoding PCEP messages, objects, and TLVs.
+ */
+
+#ifndef PCEP_ENCODING_H
+#define PCEP_ENCODING_H
+
+#include <stdbool.h>
+
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pcep_versioning {
+ bool draft_ietf_pce_segment_routing_07; /* If false, use draft16 */
+ /* As more draft versions are incorporated, add appropriate attributes
+ */
+};
+
+#define MESSAGE_HEADER_LENGTH 4
+#define PCEP_MESSAGE_LENGTH 65535
+#define OBJECT_HEADER_LENGTH 4
+#define OBJECT_RO_SUBOBJ_HEADER_LENGTH 2
+#define TLV_HEADER_LENGTH 4
+#define LENGTH_1WORD sizeof(uint32_t)
+#define LENGTH_2WORDS sizeof(uint32_t) * 2
+#define LENGTH_3WORDS sizeof(uint32_t) * 3
+#define LENGTH_4WORDS sizeof(uint32_t) * 4
+#define LENGTH_5WORDS sizeof(uint32_t) * 5
+#define LENGTH_6WORDS sizeof(uint32_t) * 6
+#define LENGTH_7WORDS sizeof(uint32_t) * 7
+#define LENGTH_8WORDS sizeof(uint32_t) * 8
+#define LENGTH_9WORDS sizeof(uint32_t) * 9
+#define LENGTH_10WORDS sizeof(uint32_t) * 10
+#define LENGTH_11WORDS sizeof(uint32_t) * 11
+#define LENGTH_12WORDS sizeof(uint32_t) * 12
+#define LENGTH_13WORDS sizeof(uint32_t) * 13
+
+/* When iterating sub-objects or TLVs, limit to 10 in case corrupt data is
+ * received */
+#define MAX_ITERATIONS 10
+
+struct pcep_versioning *create_default_pcep_versioning(void);
+void destroy_pcep_versioning(struct pcep_versioning *versioning);
+
+/*
+ * Message encoding / decoding functions
+ */
+
+/* Called before sending messages to encode the message to a byte buffer in
+ * Network byte order. This function will also encode all the objects and their
+ * TLVs in the message. The result will be stored in the encoded_message field
+ * in the pcep_message. Implemented in pcep-messages-encoding.c */
+void pcep_encode_message(struct pcep_message *message,
+ struct pcep_versioning *versioning);
+
+/* Decode the message header and return the message length.
+ * Returns < 0 for invalid message headers. */
+int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf);
+
+/* Decode the entire message */
+struct pcep_message *pcep_decode_message(const uint8_t *message_buffer);
+
+
+/*
+ * Object encoding / decoding functions
+ */
+
+/* Implemented in pcep-objects-encoding.c
+ * Encode the object in struct pcep_object_header* into the uint8_t *buf,
+ * and return the encoded object_length. */
+uint16_t pcep_encode_object(struct pcep_object_header *object_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf);
+
+/* Implemented in pcep-objects-encoding.c
+ * Decode the object, including the TLVs (if any) and return the object.
+ * Returns object on success, NULL otherwise. */
+struct pcep_object_header *pcep_decode_object(const uint8_t *msg_buf);
+
+/* Internal util functions implemented in pcep-objects-encoding.c */
+void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst);
+void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6);
+uint16_t normalize_pcep_tlv_length(uint16_t length);
+bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr);
+uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr);
+uint16_t pcep_object_get_length(enum pcep_object_classes object_class,
+ enum pcep_object_types object_type);
+
+
+/*
+ * TLV encoding / decoding functions
+ */
+
+/* Implemented in pcep-tlv-encoding.c
+ * Encode the tlv in struct pcep_tlv_header* into the uint8_t *buf,
+ * and return the encoded tlv_length. */
+uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf);
+
+/* Decode the TLV in tlv_buf and return a pointer to the object */
+struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf);
+
+
+/*
+ * utils mainly for testing purposes
+ */
+bool validate_message_objects(struct pcep_message *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_messages.c b/pceplib/pcep_msg_messages.c
new file mode 100644
index 0000000000..ec2a237f30
--- /dev/null
+++ b/pceplib/pcep_msg_messages.c
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message API.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+static struct pcep_message *
+pcep_msg_create_common_with_obj_list(enum pcep_message_types msg_type,
+ double_linked_list *obj_list)
+{
+ struct pcep_message *message =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ memset(message, 0, sizeof(struct pcep_message));
+ message->msg_header = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_message_header));
+ memset(message->msg_header, 0, sizeof(struct pcep_message_header));
+ message->msg_header->type = msg_type;
+ message->msg_header->pcep_version = PCEP_MESSAGE_HEADER_VERSION;
+ message->obj_list = ((obj_list == NULL) ? dll_initialize() : obj_list);
+
+ return message;
+}
+
+static struct pcep_message *
+pcep_msg_create_common(enum pcep_message_types msg_type)
+{
+ return pcep_msg_create_common_with_obj_list(msg_type, NULL);
+}
+
+struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN);
+ dll_append(message->obj_list,
+ pcep_obj_create_open(keepalive, deadtimer, sid, NULL));
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid, double_linked_list *tlv_list)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN);
+ dll_append(message->obj_list,
+ pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list));
+
+ return message;
+}
+
+
+struct pcep_message *
+pcep_msg_create_request(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv4 *endpoints,
+ double_linked_list *object_list)
+{
+ if ((rp == NULL) || (endpoints == NULL)) {
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREQ, object_list);
+ dll_prepend(message->obj_list, endpoints);
+ dll_prepend(message->obj_list, rp);
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_request_ipv6(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv6 *endpoints,
+ double_linked_list *object_list)
+{
+ if ((rp == NULL) || (endpoints == NULL)) {
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREQ, object_list);
+ dll_prepend(message->obj_list, endpoints);
+ dll_prepend(message->obj_list, rp);
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp,
+ double_linked_list *object_list)
+{
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCREP, object_list);
+
+ if (rp != NULL) {
+ dll_prepend(message->obj_list, rp);
+ }
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_close(uint8_t reason)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_CLOSE);
+ dll_append(message->obj_list, pcep_obj_create_close(reason));
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_error(uint8_t error_type,
+ uint8_t error_value)
+{
+ struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_ERROR);
+ dll_append(message->obj_list,
+ pcep_obj_create_error(error_type, error_value));
+
+ return message;
+}
+
+struct pcep_message *
+pcep_msg_create_error_with_objects(uint8_t error_type, uint8_t error_value,
+ double_linked_list *object_list)
+{
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_ERROR, object_list);
+ dll_prepend(message->obj_list,
+ pcep_obj_create_error(error_type, error_value));
+
+ return message;
+}
+
+struct pcep_message *pcep_msg_create_keepalive()
+{
+ return (pcep_msg_create_common(PCEP_TYPE_KEEPALIVE));
+}
+
+struct pcep_message *
+pcep_msg_create_report(double_linked_list *state_report_object_list)
+{
+ return (state_report_object_list == NULL
+ ? NULL
+ : pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_REPORT, state_report_object_list));
+}
+
+struct pcep_message *
+pcep_msg_create_update(double_linked_list *update_request_object_list)
+{
+ if (update_request_object_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update NULL update_request_object_list",
+ __func__);
+ return NULL;
+ }
+
+ /* There must be at least 3 objects:
+ * These 3 are mandatory: SRP, LSP, and ERO. The ERO may be empty */
+ if (update_request_object_list->num_entries < 3) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update there must be at least 3 update objects",
+ __func__);
+ return NULL;
+ }
+
+ double_linked_list_node *node = update_request_object_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+
+ /* Check for the mandatory first SRP object */
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) {
+ /* If the SRP object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=10 (SRP object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory first SRP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 2nd LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) {
+ /* If the LSP object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=8 (LSP object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory second LSP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 3rd ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_ERO) {
+ /* If the ERO object is missing, the receiving PCC MUST send a
+ * PCErr message with Error-type=6 (Mandatory Object missing)
+ * and Error-value=9 (ERO object missing). */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_update missing mandatory third ERO object",
+ __func__);
+ return NULL;
+ }
+
+ return (pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_UPDATE, update_request_object_list));
+}
+
+struct pcep_message *
+pcep_msg_create_initiate(double_linked_list *lsp_object_list)
+{
+ if (lsp_object_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate NULL update_request_object_list",
+ __func__);
+ return NULL;
+ }
+
+ /* There must be at least 2 objects: SRP and LSP. */
+ if (lsp_object_list->num_entries < 2) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate there must be at least 2 objects",
+ __func__);
+ return NULL;
+ }
+
+ double_linked_list_node *node = lsp_object_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+
+ /* Check for the mandatory first SRP object */
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate missing mandatory first SRP object",
+ __func__);
+ return NULL;
+ }
+
+ /* Check for the mandatory 2nd LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_create_initiate missing mandatory second LSP object",
+ __func__);
+ return NULL;
+ }
+
+ return (pcep_msg_create_common_with_obj_list(PCEP_TYPE_INITIATE,
+ lsp_object_list));
+}
+
+struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify,
+ double_linked_list *object_list)
+{
+ if (notify == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: pcep_msg_create_notify NULL notify object",
+ __func__);
+ return NULL;
+ }
+
+ struct pcep_message *message = pcep_msg_create_common_with_obj_list(
+ PCEP_TYPE_PCNOTF, object_list);
+ dll_prepend(message->obj_list, notify);
+
+ return message;
+}
diff --git a/pceplib/pcep_msg_messages.h b/pceplib/pcep_msg_messages.h
new file mode 100644
index 0000000000..8542ea10e7
--- /dev/null
+++ b/pceplib/pcep_msg_messages.h
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message API.
+ */
+
+#ifndef PCEP_MESSAGES_H
+#define PCEP_MESSAGES_H
+
+#include <stdint.h>
+#include <netinet/in.h> /* struct in_addr */
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_objects.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum pcep_message_types {
+ PCEP_TYPE_OPEN = 1,
+ PCEP_TYPE_KEEPALIVE = 2,
+ PCEP_TYPE_PCREQ = 3,
+ PCEP_TYPE_PCREP = 4,
+ PCEP_TYPE_PCNOTF = 5,
+ PCEP_TYPE_ERROR = 6,
+ PCEP_TYPE_CLOSE = 7,
+ PCEP_TYPE_REPORT = 10,
+ PCEP_TYPE_UPDATE = 11,
+ PCEP_TYPE_INITIATE = 12,
+ PCEP_TYPE_START_TLS = 13,
+ PCEP_TYPE_MAX,
+};
+
+#define PCEP_MESSAGE_HEADER_VERSION 1
+
+struct pcep_message_header {
+ uint8_t pcep_version; /* Current version is 1. */
+ enum pcep_message_types
+ type; /* Defines message type:
+ OPEN/KEEPALIVE/PCREQ/PCREP/PCNOTF/ERROR/CLOSE */
+};
+
+/* The obj_list is a double_linked_list of struct pcep_object_header pointers.
+ */
+struct pcep_message {
+ struct pcep_message_header *msg_header;
+ double_linked_list *obj_list;
+ uint8_t *encoded_message;
+ uint16_t encoded_message_length;
+};
+
+
+/*
+ * Regarding memory usage:
+ * When creating messages, any objects and tlvs passed into these APIs will be
+ * free'd when the pcep_message is free'd. That includes the
+ * double_linked_list's. So, just create the objects and TLVs, put them in their
+ * double_linked_list's, and everything will be managed internally. The message
+ * will be deleted by pcep_msg_free_message() or pcep_msg_free_message_list()
+ * which, in turn will call one of: pcep_obj_free_object() and
+ * pcep_obj_free_tlv(). For received messages, call pcep_msg_free_message() to
+ * free them.
+ */
+
+struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid);
+struct pcep_message *
+pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer,
+ uint8_t sid, double_linked_list *tlv_list);
+struct pcep_message *
+pcep_msg_create_request(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv4 *endpoints,
+ double_linked_list *object_list);
+struct pcep_message *
+pcep_msg_create_request_ipv6(struct pcep_object_rp *rp,
+ struct pcep_object_endpoints_ipv6 *endpoints,
+ double_linked_list *object_list);
+struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp,
+ double_linked_list *object_list);
+struct pcep_message *pcep_msg_create_close(uint8_t reason);
+struct pcep_message *pcep_msg_create_error(uint8_t error_type,
+ uint8_t error_value);
+struct pcep_message *pcep_msg_create_error_with_objects(
+ uint8_t error_type, uint8_t error_value,
+ double_linked_list *object_list); /* include the offending objects */
+struct pcep_message *pcep_msg_create_keepalive(void);
+struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify,
+ double_linked_list *object_list);
+
+/* Message defined in RFC 8231 section 6.1. Expecting double_linked_list of
+ * struct pcep_object_header* objects of type SRP, LSP, or path (ERO, Bandwidth,
+ * metrics, and RRO objects). */
+struct pcep_message *
+pcep_msg_create_report(double_linked_list *state_report_object_list);
+/* Message defined in RFC 8231. Expecting double_linked_list of at least 3
+ * struct pcep_object_header* objects of type SRP, LSP, and path (ERO and
+ * intended-attribute-list). The ERO must be present, but may be empty if
+ * the PCE cannot find a valid path for a delegated LSP. */
+struct pcep_message *
+pcep_msg_create_update(double_linked_list *update_request_object_list);
+/* Message defined in RFC 8281. Expecting double_linked_list of at least 2
+ * struct pcep_object_header* objects of type SRP and LSP for LSP deletion, and
+ * may also contain Endpoints, ERO and an attribute list for LSP creation. */
+struct pcep_message *
+pcep_msg_create_initiate(double_linked_list *lsp_object_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_messages_encoding.c b/pceplib/pcep_msg_messages_encoding.c
new file mode 100644
index 0000000000..23ccef480c
--- /dev/null
+++ b/pceplib/pcep_msg_messages_encoding.c
@@ -0,0 +1,351 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP messages.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+#define ANY_OBJECT 0
+#define NO_OBJECT -1
+#define NUM_CHECKED_OBJECTS 4
+/* It wont compile with this definition:
+ static const int
+ MANDATORY_MESSAGE_OBJECT_CLASSES[PCEP_TYPE_INITIATE+1][NUM_CHECKED_OBJECTS]
+ */
+static const enum pcep_object_classes MANDATORY_MESSAGE_OBJECT_CLASSES[13][4] =
+ {
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 0 */
+ {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_OPEN = 1 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_KEEPALIVE = 2 */
+ {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCREQ = 3 */
+ {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCREP = 4 */
+ {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_PCNOTF = 5 */
+ {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_ERROR = 6 */
+ {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* PCEP_TYPE_CLOSE = 7 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 8 */
+ {NO_OBJECT, NO_OBJECT, NO_OBJECT,
+ NO_OBJECT}, /* unsupported message ID = 9 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_REPORT = 10 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_UPDATE = 11 */
+ {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT,
+ ANY_OBJECT}, /* PCEP_TYPE_INITIATE = 12 */
+};
+
+/* PCEP Message Common Header, According to RFC 5440
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ver | Flags | Message-Type | Message-Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Ver (Version - 3 bits): PCEP version number. Current version is version 1.
+ *
+ * Flags (5 bits): No flags are currently defined. Unassigned bits are
+ * considered as reserved. They MUST be set to zero on transmission
+ * and MUST be ignored on receipt.
+ */
+void pcep_encode_message(struct pcep_message *message,
+ struct pcep_versioning *versioning)
+{
+ if (message == NULL) {
+ return;
+ }
+
+ if (message->msg_header == NULL) {
+ return;
+ }
+
+ /* Internal buffer used for the entire message. Later, once the entire
+ * length is known, memory will be allocated and this buffer will be
+ * copied. */
+ uint8_t message_buffer[PCEP_MESSAGE_LENGTH] = {0};
+
+ /* Write the message header. The message header length will be
+ * written when the entire length is known. */
+ uint32_t message_length = MESSAGE_HEADER_LENGTH;
+ uint16_t net_order_length = 0;
+ message_buffer[0] = (message->msg_header->pcep_version << 5) & 0xf0;
+ message_buffer[1] = message->msg_header->type;
+
+ if (message->obj_list == NULL) {
+ net_order_length = htons(message_length);
+ memcpy(message_buffer + 2, &net_order_length,
+ sizeof(net_order_length));
+ message->encoded_message =
+ pceplib_malloc(PCEPLIB_MESSAGES, message_length);
+ memcpy(message->encoded_message, message_buffer,
+ message_length);
+ message->encoded_message_length = message_length;
+
+ return;
+ }
+
+ /* Encode each of the objects */
+ double_linked_list_node *node = message->obj_list->head;
+ for (; node != NULL; node = node->next_node) {
+ message_length +=
+ pcep_encode_object(node->data, versioning,
+ message_buffer + message_length);
+ if (message_length > PCEP_MESSAGE_LENGTH) {
+ message->encoded_message = NULL;
+ message->encoded_message_length = 0;
+ return;
+ }
+ }
+
+ net_order_length = htons(message_length);
+ memcpy(message_buffer + 2, &net_order_length, sizeof(net_order_length));
+ message->encoded_message =
+ pceplib_malloc(PCEPLIB_MESSAGES, message_length);
+ memcpy(message->encoded_message, message_buffer, message_length);
+ message->encoded_message_length = message_length;
+}
+
+/*
+ * Decoding functions
+ */
+
+/* Expecting Host byte ordered header */
+static bool validate_msg_header(uint8_t msg_version, uint8_t msg_flags,
+ uint8_t msg_type, uint16_t msg_length)
+{
+ /* Invalid message if the length is less than the header
+ * size or if its not a multiple of 4 */
+ if (msg_length < MESSAGE_HEADER_LENGTH || (msg_length % 4) != 0) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PCEP message header length [%d]",
+ __func__, msg_length);
+ return false;
+ }
+
+ if (msg_version != PCEP_MESSAGE_HEADER_VERSION) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PCEP message header version [0x%x] expected version [0x%x]",
+ __func__, msg_version, PCEP_MESSAGE_HEADER_VERSION);
+ return false;
+ }
+
+ if (msg_flags != 0) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PCEP message header flags [0x%x]",
+ __func__, msg_flags);
+ return false;
+ }
+
+ switch (msg_type) {
+ /* Supported message types */
+ case PCEP_TYPE_OPEN:
+ case PCEP_TYPE_KEEPALIVE:
+ case PCEP_TYPE_PCREQ:
+ case PCEP_TYPE_PCREP:
+ case PCEP_TYPE_PCNOTF:
+ case PCEP_TYPE_ERROR:
+ case PCEP_TYPE_CLOSE:
+ case PCEP_TYPE_REPORT:
+ case PCEP_TYPE_UPDATE:
+ case PCEP_TYPE_INITIATE:
+ break;
+ default:
+ pcep_log(LOG_INFO, "%s: Invalid PCEP message header type [%d]",
+ __func__, msg_type);
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+/* Internal util function */
+static uint16_t pcep_decode_msg_header(const uint8_t *msg_buf,
+ uint8_t *msg_version, uint8_t *msg_flags,
+ uint8_t *msg_type)
+{
+ // Check RFC 5440 for version and flags position.
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //| Ver | Flags | Message-Type | Message-Length |
+ //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *msg_version = (msg_buf[0] >> 5) & 0x07;
+ *msg_flags = (msg_buf[0] & 0x1f);
+ *msg_type = msg_buf[1];
+ uint16_t host_order_length;
+ memcpy(&host_order_length, msg_buf + 2, sizeof(host_order_length));
+ return ntohs(host_order_length);
+}
+
+/* Decode the message header and return the message length */
+int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf)
+{
+ uint8_t msg_version;
+ uint8_t msg_flags;
+ uint8_t msg_type;
+ uint32_t msg_length;
+
+ msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags,
+ &msg_type);
+
+ return ((validate_msg_header(msg_version, msg_flags, msg_type,
+ msg_length)
+ == false)
+ ? -1
+ : (int32_t)msg_length);
+}
+
+bool validate_message_objects(struct pcep_message *msg)
+{
+ if (msg->msg_header->type >= PCEP_TYPE_START_TLS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unknown message type [%d]",
+ __func__, msg->msg_header->type);
+ return false;
+ }
+
+ const enum pcep_object_classes *object_classes =
+ MANDATORY_MESSAGE_OBJECT_CLASSES[msg->msg_header->type];
+ double_linked_list_node *node;
+ int index;
+ for (node = (msg->obj_list == NULL ? NULL : msg->obj_list->head),
+ index = 0;
+ index < NUM_CHECKED_OBJECTS;
+ index++, (node = (node == NULL ? NULL : node->next_node))) {
+ struct pcep_object_header *obj =
+ ((node == NULL)
+ ? NULL
+ : (struct pcep_object_header *)node->data);
+
+ if ((int)object_classes[index] == NO_OBJECT) {
+ if (node != NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unexpected object [%d] present",
+ __func__, obj->object_class);
+ return false;
+ }
+ } else if (object_classes[index] != ANY_OBJECT) {
+ if (node == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Expecting object in position [%d], but none received",
+ __func__, index);
+ return false;
+ } else if (object_classes[index] != obj->object_class) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting received message: Unexpected Object Class received [%d]",
+ __func__, object_classes[index]);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+struct pcep_message *pcep_decode_message(const uint8_t *msg_buf)
+{
+ uint8_t msg_version;
+ uint8_t msg_flags;
+ uint8_t msg_type;
+ uint16_t msg_length;
+
+ msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags,
+ &msg_type);
+
+ struct pcep_message *msg =
+ pceplib_calloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+
+ msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_message_header));
+ msg->msg_header->pcep_version = msg_version;
+ msg->msg_header->type = msg_type;
+
+ msg->obj_list = dll_initialize();
+ msg->encoded_message = pceplib_malloc(PCEPLIB_MESSAGES, msg_length);
+ memcpy(msg->encoded_message, msg_buf, msg_length);
+ msg->encoded_message_length = msg_length;
+
+ uint16_t bytes_read = MESSAGE_HEADER_LENGTH;
+ while ((msg_length - bytes_read) >= OBJECT_HEADER_LENGTH) {
+ struct pcep_object_header *obj_hdr =
+ pcep_decode_object(msg_buf + bytes_read);
+
+ if (obj_hdr == NULL) {
+ pcep_log(LOG_INFO, "%s: Discarding invalid message",
+ __func__);
+ pcep_msg_free_message(msg);
+
+ return NULL;
+ }
+
+ dll_append(msg->obj_list, obj_hdr);
+ bytes_read += obj_hdr->encoded_object_length;
+ }
+
+ if (validate_message_objects(msg) == false) {
+ pcep_log(LOG_INFO, "%s: Discarding invalid message", __func__);
+ pcep_msg_free_message(msg);
+
+ return NULL;
+ }
+
+ return msg;
+}
+
+struct pcep_versioning *create_default_pcep_versioning()
+{
+ struct pcep_versioning *versioning =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memset(versioning, 0, sizeof(struct pcep_versioning));
+
+ return versioning;
+}
+
+void destroy_pcep_versioning(struct pcep_versioning *versioning)
+{
+ pceplib_free(PCEPLIB_INFRA, versioning);
+}
diff --git a/pceplib/pcep_msg_object_error_types.c b/pceplib/pcep_msg_object_error_types.c
new file mode 100644
index 0000000000..a4fd8151cd
--- /dev/null
+++ b/pceplib/pcep_msg_object_error_types.c
@@ -0,0 +1,389 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+#include <stdlib.h>
+
+#include "pcep_msg_object_error_types.h"
+#include "pcep_utils_logging.h"
+
+/* All of these values were copied from:
+ * https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-error-object
+ * Which was last updated 2020-06-02 */
+
+static const char *error_type_strings[] = {
+ "Reserved",
+ "PCEP session establishment failure",
+ "Capability not supported",
+ "Unknown Object",
+ "Not supported object",
+ "Policy violation",
+ "Mandatory Object missing",
+ "Synchronized path computation request missing",
+ "Unknown request reference",
+ "Attempt to establish a second PCEP session",
+
+ "Reception of an invalid object", /* 10 */
+ "Unrecognized EXRS subobject",
+ "Diffserv-aware TE error",
+ "BRPC procedure completion failure",
+ "Unassigned 14",
+ "Global Concurrent Optimization Error",
+ "P2MP Capability Error",
+ "P2MP END-POINTS Error",
+ "P2MP Fragmentation Error",
+ "Invalid Operation",
+
+ "LSP State Synchronization Error", /* 20 */
+ "Invalid traffic engineering path setup type",
+ "Unassigned 22",
+ "Bad parameter value",
+ "LSP instantiation error",
+ "PCEP StartTLS failure",
+ "Association Error",
+ "WSON RWA Error",
+ "H-PCE Error",
+ "Path computation failure",
+ "Unassigned 30"};
+
+static const char *error_value_strings[MAX_ERROR_TYPE][MAX_ERROR_VALUE] = {
+
+ /* 0 Reserved */
+ {"Unassigned"},
+
+ /* 1 PCEP session establishment failure */
+ {
+ "Unassigned",
+ "reception of an invalid Open message or a non Open message.",
+ "no Open message received before the expiration of the OpenWait timer",
+ "unacceptable and non negotiable session characteristics",
+ "unacceptable but negotiable session characteristics",
+ "reception of a second Open message with still unacceptable session characteristics",
+ "reception of a PCErr message proposing unacceptable session characteristics",
+ "No Keepalive or PCErr message received before the expiration of the KeepWait timer",
+ "PCEP version not supported",
+ },
+
+ /* 2 Capability not supported */
+ {"Unassigned"},
+
+ /* 3 Unknown Object */
+ {
+ "Unassigned",
+ "Unrecognized object class",
+ "Unrecognized object Type",
+ },
+
+ /* 4 Not supported object */
+ {
+ "Unassigned",
+ "Not supported object class",
+ "Not supported object Type",
+ "Unassigned",
+ "Unsupported parameter",
+ "Unsupported network performance constraint",
+ "Bandwidth Object type 3 or 4 not supported",
+ "Unsupported endpoint type in END-POINTS Generalized Endpoint object type",
+ "Unsupported TLV present in END-POINTS Generalized Endpoint object type",
+ "Unsupported granularity in the RP object flags",
+ },
+
+ /* 5 Policy violation */
+ {
+ "Unassigned",
+ "C bit of the METRIC object set (request rejected)",
+ "O bit of the RP object cleared (request rejected)",
+ "objective function not allowed (request rejected)",
+ "OF bit of the RP object set (request rejected)",
+ "Global concurrent optimization not allowed",
+ "Monitoring message supported but rejected due to policy violation",
+ "P2MP Path computation is not allowed",
+ "Not allowed network performance constraint",
+ },
+
+ /* 6 Mandatory Object missing */
+ {
+ "Unassigned",
+ "RP object missing",
+ "RRO missing for a reoptimization request (R bit of the RP object set)",
+ "END-POINTS object missing",
+ "MONITORING object missing",
+ "Unassigned",
+ "Unassigned",
+ "Unassigned",
+ "LSP object missing",
+ "ERO object missing",
+ "SRP object missing",
+ "LSP-IDENTIFIERS TLV missing",
+ "LSP-DB-VERSION TLV missing",
+ "S2LS object missing",
+ "P2MP-LSP-IDENTIFIERS TLV missing",
+ "DISJOINTNESS-CONFIGURATION TLV missing",
+ },
+
+ /* 7 Synchronized path computation request missing */
+ {"Unassigned"},
+
+ /* 8 Unknown request reference */
+ {"Unassigned"},
+
+ /* 9 Attempt to establish a second PCEP session */
+ {"Unassigned"},
+
+ /* 10 Reception of an invalid object */
+ {
+ "Unassigned",
+ "reception of an object with P flag not set although the P-flag must be set according to this specification.",
+ "Bad label value",
+ "Unsupported number of SR-ERO subobjects",
+ "Bad label format",
+ "ERO mixes SR-ERO subobjects with other subobject types",
+ "Both SID and NAI are absent in the SR-ERO subobject",
+ "Both SID and NAI are absent in the SR-RRO subobject",
+ "SYMBOLIC-PATH-NAME TLV missing",
+ "MSD exceeds the default for the PCEP session",
+ "RRO mixes SR-RRO subobjects with other subobject types",
+ "Malformed object",
+ "Missing PCE-SR-CAPABILITY sub-TLV",
+ "Unsupported NAI Type in the SR-ERO/SR-RRO subobject",
+ "Unknown SID",
+ "NAI cannot be resolved to a SID",
+ "Could not find SRGB",
+ "SID index exceeds SRGB size",
+ "Could not find SRLB",
+ "SID index exceeds SRLB size",
+ "Inconsistent SIDs in SR-ERO / SR-RRO subobjects",
+ "MSD must be nonzero",
+ "Mismatch of O field in S2LS and LSP object",
+ "Incompatible OF codes in H-PCE",
+ "Bad Bandwidth Object type 3 (Generalized bandwidth) or 4 (Generalized bandwidth of existing TE-LSP for which a reoptimization is requested)",
+ "Unsupported LSP Protection Flags in PROTECTION-ATTRIBUTE TLV",
+ "Unsupported Secondary LSP Protection Flags in PROTECTION-ATTRIBUTE TLV",
+ "Unsupported Link Protection Type in PROTECTION-ATTRIBUTE TLV",
+ "LABEL-SET TLV present with 0 bit set but without R bit set in RP",
+ "Wrong LABEL-SET TLV present with 0 and L bit set",
+ "Wrong LABEL-SET with O bit set and wrong format",
+ "Missing GMPLS-CAPABILITY TLV",
+ "Incompatible OF code",
+ },
+
+ /* 11 Unrecognized EXRS subobject */
+ {"Unassigned"},
+
+ /* 12 Diffserv-aware TE error */
+ {
+ "Unassigned",
+ "Unsupported class-type",
+ "Invalid class-type",
+ "Class-Type and setup priority do not form a configured TE-class",
+ },
+
+ /* 13 BRPC procedure completion failure */
+ {
+ "Unassigned",
+ "BRPC procedure not supported by one or more PCEs along the domain path",
+ },
+
+ /* 14 Unassigned */
+ {"Unassigned"},
+
+ /* 15 Global Concurrent Optimization Error */
+ {
+ "Unassigned",
+ "Insufficient memory",
+ "Global concurrent optimization not supported",
+ },
+
+ /* 16 P2MP Capability Error */
+ {
+ "Unassigned",
+ "The PCE cannot satisfy the request due to insufficient memory",
+ "The PCE is not capable of P2MP computation",
+ },
+
+ /* 17 P2MP END-POINTS Error */
+ {
+ "Unassigned",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 2",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 3",
+ "The PCE cannot satisfy the request due to no END-POINTS with leaf type 4",
+ "The PCE cannot satisfy the request due to inconsistent END-POINTS",
+ },
+
+ /* 18 P2MP Fragmentation Error */
+ {
+ "Unassigned",
+ "Fragmented request failure",
+ "Fragmented Report failure",
+ "Fragmented Update failure",
+ "Fragmented Instantiation failure",
+ },
+
+ /* 19 Invalid Operation */
+ {
+ "Unassigned",
+ "Attempted LSP Update Request for a non-delegated LSP. The PCEP-ERROR object is followed by the LSP object that identifies the LSP.",
+ "Attempted LSP Update Request if the stateful PCE capability was not advertised.",
+ "Attempted LSP Update Request for an LSP identified by an unknown PLSP-ID.",
+ "Unassigned",
+ "Attempted LSP State Report if active stateful PCE capability was not advertised.",
+ "PCE-initiated LSP limit reached",
+ "Delegation for PCE-initiated LSP cannot be revoked",
+ "Non-zero PLSP-ID in LSP Initiate Request",
+ "LSP is not PCE initiated",
+ "PCE-initiated operation-frequency limit reached",
+ "Attempted LSP State Report for P2MP if stateful PCE capability for P2MP was not advertised",
+ "Attempted LSP Update Request for P2MP if active stateful PCE capability for P2MP was not advertised",
+ "Attempted LSP Instantiation Request for P2MP if stateful PCE instantiation capability for P2MP was not advertised",
+ "Auto-Bandwidth capability was not advertised",
+ },
+
+ /* 20 LSP State Synchronization Error */
+ {
+ "Unassigned",
+ "A PCE indicates to a PCC that it cannot process (an otherwise valid) LSP State Report. The PCEP- ERROR object is followed by the LSP object that identifies the LSP.",
+ "LSP-DB version mismatch.",
+ "Attempt to trigger synchronization before PCE trigger.",
+ "Attempt to trigger a synchronization when the PCE triggered synchronization capability has not been advertised.",
+ "A PCC indicates to a PCE that it cannot complete the State Synchronization.",
+ "Received an invalid LSP-DB Version Number.",
+ "Received an invalid Speaker Entity Identifier.",
+ },
+
+ /* 21 Invalid traffic engineering path setup type */
+ {
+ "Unassigned",
+ "Unsupported path setup type",
+ "Mismatched path setup type",
+ },
+
+ /* 22 Unassigned */
+ {"Unassigned"},
+
+ /* 23 Bad parameter value */
+ {
+ "Unassigned",
+ "SYMBOLIC-PATH-NAME in use",
+ "Speaker identity included for an LSP that is not PCE initiated",
+ },
+
+ /* 24 LSP instantiation error */
+ {
+ "Unassigned",
+ "Unacceptable instantiation parameters",
+ "Internal error",
+ "Signaling error",
+ },
+
+ /* 25 PCEP StartTLS failure */
+ {
+ "Unassigned",
+ "Reception of StartTLS after any PCEP exchange",
+ "Reception of any other message apart from StartTLS, Open, or PCErr",
+ "Failure, connection without TLS is not possible",
+ "Failure, connection without TLS is possible",
+ "No StartTLS message (nor PCErr/Open) before StartTLSWait timer expiry",
+ },
+
+ /* 26 Association Error */
+ {
+ "Unassigned",
+ "Association Type is not supported",
+ "Too many LSPs in the association group",
+ "Too many association groups",
+ "Association unknown",
+ "Operator-configured association information mismatch",
+ "Association information mismatch",
+ "Cannot join the association group",
+ "Association ID not in range",
+ "Tunnel ID or End points mismatch for Path Protection Association",
+ "Attempt to add another working/protection LSP for Path Protection Association",
+ "Protection type is not supported",
+ },
+
+ /* 27 WSON RWA Error */
+ {
+ "Unassigned",
+ "Insufficient Memory",
+ "RWA computation Not supported",
+ "Syntactical Encoding error",
+ },
+
+ /* 28 H-PCE Error */
+ {
+ "Unassigned",
+ "H-PCE Capability not advertised",
+ "Parent PCE Capability cannot be provided",
+ },
+
+ /* 29 Path computation failure */
+ {
+ "Unassigned",
+ "Unacceptable request message",
+ "Generalized bandwidth value not supported",
+ "Label Set constraint could not be met",
+ "Label constraint could not be met",
+ }
+
+ /* 30-255 Unassigned */
+};
+
+
+const char *get_error_type_str(enum pcep_error_type error_type)
+{
+ if (error_type < 0 || error_type >= MAX_ERROR_TYPE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_type_str: error_type [%d] out of range [0..%d]",
+ __func__, error_type, MAX_ERROR_TYPE);
+
+ return NULL;
+ }
+
+ return error_type_strings[error_type];
+}
+
+const char *get_error_value_str(enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ if (error_type < 0 || error_type >= MAX_ERROR_TYPE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_value_str: error_type [%d] out of range [0..%d]",
+ __func__, error_type, MAX_ERROR_TYPE);
+
+ return NULL;
+ }
+
+ if (error_value < 0 || error_value >= MAX_ERROR_VALUE) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: get_error_value_str: error_value [%d] out of range [0..%d]",
+ __func__, error_value, MAX_ERROR_VALUE);
+
+ return NULL;
+ }
+
+ if (error_value_strings[error_type][error_value] == NULL) {
+ return "Unassigned";
+ }
+
+ return error_value_strings[error_type][error_value];
+}
diff --git a/pceplib/pcep_msg_object_error_types.h b/pceplib/pcep_msg_object_error_types.h
new file mode 100644
index 0000000000..d62cc7e277
--- /dev/null
+++ b/pceplib/pcep_msg_object_error_types.h
@@ -0,0 +1,284 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * Error Object Type and Value definitions
+ */
+
+#ifndef PCEP_OBJECT_ERROR_TYPES_H
+#define PCEP_OBJECT_ERROR_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_ERROR_TYPE 30
+#define MAX_ERROR_VALUE 255
+
+enum pcep_error_type {
+ PCEP_ERRT_SESSION_FAILURE = 1,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED = 2,
+ PCEP_ERRT_UNKNOW_OBJECT = 3,
+ PCEP_ERRT_NOT_SUPPORTED_OBJECT = 4,
+ PCEP_ERRT_POLICY_VIOLATION = 5,
+ PCEP_ERRT_MANDATORY_OBJECT_MISSING = 6,
+ PCEP_ERRT_SYNC_PC_REQ_MISSING = 7,
+ PCEP_ERRT_UNKNOWN_REQ_REF = 8,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION = 9,
+ PCEP_ERRT_RECEPTION_OF_INV_OBJECT = 10,
+
+ PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ = 11,
+ PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12,
+ PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13,
+ PCEP_ERRT_UNASSIGNED14 = 14,
+ PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15,
+ PCEP_ERRT_P2PMP_CAP_ERROR = 16,
+ PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17,
+ PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18,
+ PCEP_ERRT_INVALID_OPERATION = 19,
+ PCEP_ERRT_LSP_STATE_SYNC_ERROR = 20,
+
+ PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21,
+ PCEP_ERRT_UNASSIGNED22 = 22,
+ PCEP_ERRT_BAD_PARAMETER_VALUE = 23,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR = 24,
+ PCEP_ERRT_START_TLS_FAILURE = 25,
+ PCEP_ERRT_ASSOCIATION_ERROR = 26,
+ PCEP_ERRT_WSON_RWA_ERROR = 27,
+ PCEP_ERRT_H_PCE_ERROR = 28,
+ PCEP_ERRT_PATH_COMP_FAILURE = 29,
+ PCEP_ERRT_UNASSIGNED30 = 30 /* 30 - 255 Unassigned */
+};
+
+enum pcep_error_value {
+ /* Error Value for Error Types that do not use an Error Value:
+ * PCEP_ERRT_CAPABILITY_NOT_SUPPORTED=2
+ * PCEP_ERRT_SYNC_PC_REQ_MISSING=7
+ * PCEP_ERRT_UNKNOWN_REQ_REF=8
+ * PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION=9
+ * PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ=11 */
+ PCEP_ERRV_UNASSIGNED = 0,
+
+ /* Error Values for PCEP_ERRT_SESSION_FAILURE=1 */
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG = 1,
+ PCEP_ERRV_OPENWAIT_TIMED_OUT = 2,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NO_NEG = 3,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG = 4,
+ PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE = 5,
+ PCEP_ERRV_RECVD_PCERR = 6,
+ PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT = 7,
+ PCEP_ERRV_PCEP_VERSION_NOT_SUPPORTED = 8,
+
+ /* Error Values for PCEP_ERRT_UNKNOW_OBJECT=3 */
+ PCEP_ERRV_UNREC_OBJECT_CLASS = 1,
+ PCEP_ERRV_UNREC_OBJECT_TYPE = 2,
+
+ /* Error Values for PCEP_ERRT_NOT_SUPPORTED_OBJECT=4 */
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_CLASS = 1,
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_TYPE = 2,
+ /* 3: Unassigned */
+ PCEP_ERRV_UNSUPPORTED_PARAM = 4,
+ PCEP_ERRV_UNSUPPORTED_NW_PERF_CONSTRAINT = 5,
+ PCEP_ERRV_NOT_SUPPORTED_BW_OBJECT_3_4 = 6,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TYPE = 7,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TLV = 8,
+ PCEP_ERRV_UNSUPPORTED_RP_FLAG_GRANULARITY = 9,
+
+ /* Error Values for PCEP_ERRT_POLICY_VIOLATION=5 */
+ PCEP_ERRV_C_BIT_SET_IN_METRIC_OBJECT = 1,
+ PCEP_ERRV_O_BIT_CLEARD_IN_RP_OBJECT = 2,
+ PCEP_ERRV_OBJECTIVE_FUNC_NOT_ALLOWED = 3,
+ PCEP_ERRV_RP_OF_BIT_SET = 4,
+ PCEP_ERRV_GLOBAL_CONCURRENCY_NOT_ALLOWED = 5,
+ PCEP_ERRV_MONITORING_MSG_REJECTED = 6,
+ PCEP_ERRV_P2MP_PATH_COMP_NOT_ALLOWED = 7,
+ PCEP_ERRV_UNALLOWED_NW_PERF_CONSTRAINT = 8,
+
+ /* Error Values for PCEP_ERRT_MANDATORY_OBJECT_MISSING=6 */
+ PCEP_ERRV_RP_OBJECT_MISSING = 1,
+ PCEP_ERRV_RRO_OBJECT_MISSING_FOR_REOP = 2,
+ PCEP_ERRV_EP_OBJECT_MISSING = 3,
+ PCEP_ERRV_MONITOR_OBJECT_MISSING = 4,
+ /* 5 - 7 Unassigned */
+ PCEP_ERRV_LSP_OBJECT_MISSING = 8,
+ PCEP_ERRV_ERO_OBJECT_MISSING = 9,
+ PCEP_ERRV_SRP_OBJECT_MISSING = 10,
+ PCEP_ERRV_LSP_ID_TLV_MISSING = 11,
+ PCEP_ERRV_LSP_DB_TLV_MISSING = 12,
+ PCEP_ERRV_S2LS_OBJECT_MISSING = 13,
+ PCEP_ERRV_P2MP_LSP_ID_TLV_MISSING = 14,
+ PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING = 15,
+
+ /* Error Values for PCEP_ERRT_RECEPTION_OF_INV_OBJECT=10 */
+ PCEP_ERRV_P_FLAG_NOT_CORRECT_IN_OBJECT = 1,
+ PCEP_ERRV_BAD_LABEL_VALUE = 2,
+ PCEP_ERRV_UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS = 3,
+ PCEP_ERRV_BAD_LABEL_FORMAT = 4,
+ PCEP_ERRV_ERO_SR_ERO_MIX = 5,
+ PCEP_ERRV_SR_ERO_SID_NAI_ABSENT = 6,
+ PCEP_ERRV_SR_RRO_SID_NAI_ABSENT = 7,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING = 8,
+ PCEP_ERRV_MSD_EXCEEDS_PCEP_SESSION_MAX = 9,
+
+ PCEP_ERRV_RRO_SR_RRO_MIX = 10,
+ PCEP_ERRV_MALFORMED_OBJECT = 11,
+ PCEP_ERRV_MISSING_PCE_SR_CAP_TLV = 12,
+ PCEP_ERRV_UNSUPPORTED_NAI = 13,
+ PCEP_ERRV_UNKNOWN_SID = 14,
+ PCEP_ERRV_CANNOT_RESOLVE_NAI_TO_SID = 15,
+ PCEP_ERRV_COULD_NOT_FIND_SRGB = 16,
+ PCEP_ERRV_SID_EXCEEDS_SRGB = 17,
+ PCEP_ERRV_COULD_NOT_FIND_SRLB = 18,
+ PCEP_ERRV_SID_EXCEEDS_SRLB = 19,
+
+ PCEP_ERRV_INCONSISTENT_SID = 20,
+ PCEP_ERRV_MSD_MUST_BE_NONZERO = 21,
+ PCEP_ERRV_MISMATCH_O_S2LS_LSP = 22,
+ PCEP_ERRV_INCOMPATIBLE_H_PCE_OF = 23,
+ PCEP_ERRV_BAD_BANDWIDTH_TYPE_3_4 = 24,
+ PCEP_ERRV_UNSUPPORTED_LSP_PROT_FLAGS = 25,
+ PCEP_ERRV_UNSUPPORTED_2ND_LSP_PROT_FLAGS = 26,
+ PCEP_ERRV_UNSUPPORTED_LINK_PROT_TYPE = 27,
+ PCEP_ERRV_LABEL_SET_TLV_NO_RP_R = 28,
+ PCEP_ERRV_WRONG_LABEL_SET_TLV_O_L_SET = 29,
+
+ PCEP_ERRV_WRONG_LABEL_SET_O_SET = 30,
+ PCEP_ERRV_MISSING_GMPLS_CAP_TLV = 31,
+ PCEP_ERRV_INCOMPATIBLE_OF_CODE = 32,
+
+ /* PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12 */
+ PCEP_ERRV_UNSUPPORTED_CLASS_TYPE = 1,
+ PCEP_ERRV_INVALID_CLASS_TYPE = 2,
+ PCEP_ERRV_CLASS_SETUP_TYPE_NOT_TE_CLASS = 3,
+
+ /* PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13 */
+ PCEP_ERRV_BRPC_PROC_NOT_SUPPORTED = 1,
+
+ /* PCEP_ERRT_UNASSIGNED14 = 14 */
+
+ /* PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15 */
+ PCEP_ERRV_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED = 2,
+
+ /* PCEP_ERRT_P2PMP_CAP_ERROR = 16 */
+ PCEP_ERRV_PCE_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_PCE_NOT_CAPABLE_P2MP_COMP = 2,
+
+ /* PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17 */
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE2 = 1,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE3 = 2,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE4 = 3,
+ PCEP_ERRV_INCONSITENT_EP = 4,
+
+ /* PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18 */
+ PCEP_ERRV_FRAG_REQUEST_FAILURE = 1,
+ PCEP_ERRV_FRAG_REPORT_FAILURE = 2,
+ PCEP_ERRV_FRAG_UPDATE_FAILURE = 3,
+ PCEP_ERRV_FRAG_INSTANTIATION_FAILURE = 4,
+
+ /* Error Values for PCEP_ERRT_INVALID_OPERATION=19 */
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP = 1,
+ PCEP_ERRV_LSP_UPDATE_NON_ADVERTISED_PCE = 2,
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID = 3,
+ /* 4: unassigned */
+ PCEP_ERRV_LSP_REPORT_NON_ADVERTISED_PCE = 5,
+ PCEP_ERRV_PCE_INIT_LSP_LIMIT_REACHED = 6,
+ PCEP_ERRV_PCE_INIT_LSP_DELEGATION_CANT_REVOKE = 7,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID = 8,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED = 9,
+ PCEP_ERRV_PCE_INIT_OP_FREQ_LIMIT_REACHED = 10,
+ PCEP_ERRV_LSP_REPORT_P2MP_NOT_ADVERTISED = 11,
+ PCEP_ERRV_LSP_UPDATE_P2MP_NOT_ADVERTISED = 12,
+ PCEP_ERRV_LSP_INSTANTIATION_P2MP_NOT_ADVERTISED = 13,
+ PCEP_ERRV_AUTO_BW_CAP_NOT_ADVERTISED = 14,
+
+ /* Error Values for PCEP_ERRT_LSP_STATE_SYNC_ERROR=20 */
+ PCEP_ERRV_PCE_CANT_PROCESS_LSP_REPORT = 1,
+ PCEP_ERRV_LSP_DB_VERSION_MISMATCH = 2,
+ PCEP_ERRV_TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER = 3,
+ PCEP_ERRV_TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP = 4,
+ PCEP_ERRV_PCC_CANT_COMPLETE_STATE_SYNC = 5,
+ PCEP_ERRV_INVALID_LSP_DB_VERSION_NUMBER = 6,
+ PCEP_ERRV_INVALID_SPEAKER_ENTITY_ID = 7,
+
+ /* PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21 */
+ PCEP_ERRV_UNSUPPORTED_PATH_SETUP_TYPE = 1,
+ PCEP_ERRV_MISMATCHED_PATH_SETUP_TYPE = 2,
+
+ /* PCEP_ERRT_UNASSIGNED22 = 22 */
+
+ /* Error Values for PCEP_ERRT_BAD_PARAMETER_VALUE=23 */
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_IN_USE = 1,
+ PCEP_ERRV_LSP_SPEAKER_ID_NOT_PCE_INITIATED = 2,
+
+ /* Error Values for PCEP_ERRT_LSP_INSTANTIATE_ERROR=24 */
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR = 1,
+ PCEP_ERRV_INTERNAL_ERROR = 2,
+ PCEP_ERRV_SIGNALLING_ERROR = 3,
+
+ /* PCEP_ERRT_START_TLS_FAILURE = 25 */
+ PCEP_ERRV_START_TLS_AFTER_PCEP_EXCHANGE = 1,
+ PCEP_ERRV_MSG_NOT_START_TLS_OPEN_ERROR = 2,
+ PCEP_ERRV_CONNECTION_WO_TLS_NOT_POSSIBLE = 3,
+ PCEP_ERRV_CONNECTION_WO_TLS_IS_POSSIBLE = 4,
+ PCEP_ERRV_NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER = 5,
+
+ /* PCEP_ERRT_ASSOCIATION_ERROR = 26 */
+ PCEP_ERRV_ASSOC_TYPE_NOT_SUPPORTED = 1,
+ PCEP_ERRV_TOO_MANY_LSPS_IN_ASSOC_GRP = 2,
+ PCEP_ERRV_TOO_MANY_ASSOC_GROUPS = 3,
+ PCEP_ERRV_ASSOCIATION_UNKNOWN = 4,
+ PCEP_ERRV_OP_CONF_ASSOC_INFO_MISMATCH = 5,
+ PCEP_ERRV_ASSOC_INFO_MISMATCH = 6,
+ PCEP_ERRV_CANNOT_JOIN_ASSOC_GROUP = 7,
+ PCEP_ERRV_ASSOC_ID_NOT_IN_RANGE = 8,
+ PCEP_ERRV_TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC = 9,
+ PCEP_ERRV_ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC = 10,
+ PCEP_ERRV_PROTECTION_TYPE_NOT_SUPPORTED = 11,
+
+ /* PCEP_ERRT_WSON_RWA_ERROR = 27 */
+ PCEP_ERRV_RWA_INSUFFICIENT_MEMORY = 1,
+ PCEP_ERRV_RWA_COMP_NOT_SUPPORTED = 2,
+ PCEP_ERRV_SYNTAX_ENC_ERROR = 3,
+
+ /* PCEP_ERRT_H_PCE_ERROR = 28 */
+ PCEP_ERRV_H_PCE_CAP_NOT_ADVERTISED = 1,
+ PCEP_ERRV_PARENT_PCE_CAP_CANT_BE_PROVIDED = 2,
+
+ /* PCEP_ERRT_PATH_COMP_FAILURE = 29 */
+ PCEP_ERRV_UNACCEPTABLE_REQUEST_MSG = 1,
+ PCEP_ERRV_GENERALIZED_BW_VAL_NOT_SUPPORTED = 2,
+ PCEP_ERRV_LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET = 3,
+ PCEP_ERRV_LABEL_CONSTRAINT_COULD_NOT_BE_MET = 4,
+
+};
+
+const char *get_error_type_str(enum pcep_error_type error_type);
+const char *get_error_value_str(enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_objects.c b/pceplib/pcep_msg_objects.c
new file mode 100644
index 0000000000..6c943ddc2a
--- /dev/null
+++ b/pceplib/pcep_msg_objects.c
@@ -0,0 +1,854 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message object API.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* Internal common function used to create a pcep_object and populate the header
+ */
+static struct pcep_object_header *pcep_obj_create_common_with_tlvs(
+ uint8_t obj_length, enum pcep_object_classes object_class,
+ enum pcep_object_types object_type, double_linked_list *tlv_list)
+{
+ uint8_t *buffer = pceplib_malloc(PCEPLIB_MESSAGES, obj_length);
+ memset(buffer, 0, obj_length);
+
+ /* The flag_p and flag_i flags will be set externally */
+ struct pcep_object_header *hdr = (struct pcep_object_header *)buffer;
+ hdr->object_class = object_class;
+ hdr->object_type = object_type;
+ hdr->tlv_list = tlv_list;
+
+ return hdr;
+}
+
+static struct pcep_object_header *
+pcep_obj_create_common(uint8_t obj_length,
+ enum pcep_object_classes object_class,
+ enum pcep_object_types object_type)
+{
+ return pcep_obj_create_common_with_tlvs(obj_length, object_class,
+ object_type, NULL);
+}
+
+struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive,
+ uint8_t deadtimer, uint8_t sid,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_open *open =
+ (struct pcep_object_open *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_open), PCEP_OBJ_CLASS_OPEN,
+ PCEP_OBJ_TYPE_OPEN, tlv_list);
+
+ open->open_version =
+ PCEP_OBJECT_OPEN_VERSION; /* PCEP version. Current version is 1
+ /No flags are currently defined. */
+ open->open_keepalive =
+ keepalive; /* Maximum period of time between two consecutive
+ PCEP messages sent by the sender. */
+ open->open_deadtimer = deadtimer; /* Specifies the amount of time before
+ closing the session down. */
+ open->open_sid = sid; /* PCEP session number that identifies the current
+ session. */
+
+ return open;
+}
+
+struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r,
+ bool flag_b, bool flag_s,
+ bool flag_of, uint32_t reqid,
+ double_linked_list *tlv_list)
+{
+ if (priority > OBJECT_RP_MAX_PRIORITY) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Error creating RP object, invalid priority [%d], max priority [%d].",
+ __func__, priority, OBJECT_RP_MAX_PRIORITY);
+ return NULL;
+ }
+
+ struct pcep_object_rp *obj =
+ (struct pcep_object_rp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_rp), PCEP_OBJ_CLASS_RP,
+ PCEP_OBJ_TYPE_RP, tlv_list);
+
+ obj->priority = priority;
+ obj->flag_reoptimization = flag_r;
+ obj->flag_bidirectional = flag_b;
+ obj->flag_strict = flag_s;
+ obj->flag_of = flag_of;
+ obj->request_id = reqid;
+
+ return obj;
+}
+
+struct pcep_object_notify *
+pcep_obj_create_notify(enum pcep_notification_types notification_type,
+ enum pcep_notification_values notification_value)
+{
+ struct pcep_object_notify *obj =
+ (struct pcep_object_notify *)pcep_obj_create_common(
+ sizeof(struct pcep_object_notify), PCEP_OBJ_CLASS_NOTF,
+ PCEP_OBJ_TYPE_NOTF);
+
+ obj->notification_type = notification_type;
+ obj->notification_value = notification_value;
+
+ return obj;
+}
+
+struct pcep_object_nopath *
+pcep_obj_create_nopath(uint8_t ni, bool flag_c,
+ enum pcep_nopath_tlv_err_codes error_code)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ pcep_tlv_create_nopath_vector(error_code);
+ double_linked_list *tlv_list = dll_initialize();
+ dll_append(tlv_list, tlv);
+
+ struct pcep_object_nopath *obj =
+ (struct pcep_object_nopath *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_nopath),
+ PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH, tlv_list);
+
+ obj->ni = ni;
+ obj->flag_c = flag_c;
+ obj->err_code = error_code;
+
+ return obj;
+}
+
+struct pcep_object_association_ipv4 *
+pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in_addr src)
+{
+ struct pcep_object_association_ipv4 *obj =
+ (struct pcep_object_association_ipv4 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_association_ipv4),
+ PCEP_OBJ_CLASS_ASSOCIATION,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4);
+
+ obj->R_flag = r_flag;
+ obj->association_type = association_type;
+ obj->association_id = association_id;
+ obj->src = src;
+
+ return obj;
+}
+struct pcep_object_association_ipv6 *
+pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in6_addr src)
+{
+ struct pcep_object_association_ipv6 *obj =
+ (struct pcep_object_association_ipv6 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_association_ipv6),
+ PCEP_OBJ_CLASS_ASSOCIATION,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6);
+
+ obj->R_flag = r_flag;
+ obj->association_type = association_type;
+ obj->association_id = association_id;
+ obj->src = src;
+
+ return obj;
+}
+struct pcep_object_endpoints_ipv4 *
+pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4,
+ const struct in_addr *dst_ipv4)
+{
+ if (src_ipv4 == NULL || dst_ipv4 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_endpoints_ipv4 *obj =
+ (struct pcep_object_endpoints_ipv4 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_endpoints_ipv4),
+ PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+
+ obj->src_ipv4.s_addr = src_ipv4->s_addr;
+ obj->dst_ipv4.s_addr = dst_ipv4->s_addr;
+
+ return obj;
+}
+
+struct pcep_object_endpoints_ipv6 *
+pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6,
+ const struct in6_addr *dst_ipv6)
+{
+ if (src_ipv6 == NULL || dst_ipv6 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_endpoints_ipv6 *obj =
+ (struct pcep_object_endpoints_ipv6 *)pcep_obj_create_common(
+ sizeof(struct pcep_object_endpoints_ipv6),
+ PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV6);
+
+ memcpy(&obj->src_ipv6, src_ipv6, sizeof(struct in6_addr));
+ memcpy(&obj->dst_ipv6, dst_ipv6, sizeof(struct in6_addr));
+
+ return obj;
+}
+
+struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth)
+{
+ struct pcep_object_bandwidth *obj =
+ (struct pcep_object_bandwidth *)pcep_obj_create_common(
+ sizeof(struct pcep_object_bandwidth),
+ PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ);
+
+ obj->bandwidth = bandwidth;
+
+ return obj;
+}
+
+struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type,
+ bool flag_b, bool flag_c,
+ float value)
+{
+ struct pcep_object_metric *obj =
+ (struct pcep_object_metric *)pcep_obj_create_common(
+ sizeof(struct pcep_object_metric),
+ PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC);
+
+ obj->flag_b = flag_b;
+ obj->flag_c = flag_c;
+ obj->type = type;
+ obj->value = value;
+
+ return obj;
+}
+
+struct pcep_object_lspa *
+pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any,
+ uint32_t include_all, uint8_t setup_priority,
+ uint8_t holding_priority, bool flag_local_protection)
+{
+ struct pcep_object_lspa *obj =
+ (struct pcep_object_lspa *)pcep_obj_create_common(
+ sizeof(struct pcep_object_lspa), PCEP_OBJ_CLASS_LSPA,
+ PCEP_OBJ_TYPE_LSPA);
+
+ obj->lspa_exclude_any = exclude_any;
+ obj->lspa_include_any = include_any;
+ obj->lspa_include_all = include_all;
+ obj->setup_priority = setup_priority;
+ obj->holding_priority = holding_priority;
+ obj->flag_local_protection = flag_local_protection;
+
+ return obj;
+}
+
+struct pcep_object_svec *
+pcep_obj_create_svec(bool srlg, bool node, bool link,
+ double_linked_list *request_id_list)
+{
+ if (request_id_list == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_svec *obj =
+ (struct pcep_object_svec *)pcep_obj_create_common(
+ sizeof(struct pcep_object_svec), PCEP_OBJ_CLASS_SVEC,
+ PCEP_OBJ_TYPE_SVEC);
+
+ obj->flag_srlg_diverse = srlg;
+ obj->flag_node_diverse = node;
+ obj->flag_link_diverse = link;
+ obj->request_id_list = request_id_list;
+
+ return obj;
+}
+
+struct pcep_object_error *
+pcep_obj_create_error(enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ struct pcep_object_error *obj =
+ (struct pcep_object_error *)pcep_obj_create_common(
+ sizeof(struct pcep_object_error), PCEP_OBJ_CLASS_ERROR,
+ PCEP_OBJ_TYPE_ERROR);
+
+ obj->error_type = error_type;
+ obj->error_value = error_value;
+
+ return obj;
+}
+
+struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason)
+{
+ struct pcep_object_close *obj =
+ (struct pcep_object_close *)pcep_obj_create_common(
+ sizeof(struct pcep_object_close), PCEP_OBJ_CLASS_CLOSE,
+ PCEP_OBJ_TYPE_CLOSE);
+
+ obj->reason = reason;
+
+ return obj;
+}
+
+struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove,
+ uint32_t srp_id_number,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_srp *obj =
+ (struct pcep_object_srp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_srp), PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_TYPE_SRP, tlv_list);
+
+ obj->flag_lsp_remove = lsp_remove;
+ obj->srp_id_number = srp_id_number;
+
+ return obj;
+}
+
+struct pcep_object_lsp *
+pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status,
+ bool c_flag, bool a_flag, bool r_flag, bool s_flag,
+ bool d_flag, double_linked_list *tlv_list)
+{
+ /* The plsp_id is only 20 bits */
+ if (plsp_id > MAX_PLSP_ID) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_obj_create_lsp invalid plsp_id [%d] max value [%d]",
+ __func__, plsp_id, MAX_PLSP_ID);
+ return NULL;
+ }
+
+ /* The status is only 3 bits */
+ if (status > MAX_LSP_STATUS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_obj_create_lsp invalid status [%d] max value [%d]",
+ __func__, plsp_id, MAX_PLSP_ID);
+ return NULL;
+ }
+
+ struct pcep_object_lsp *obj =
+ (struct pcep_object_lsp *)pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_lsp), PCEP_OBJ_CLASS_LSP,
+ PCEP_OBJ_TYPE_LSP, tlv_list);
+
+ obj->plsp_id = plsp_id;
+ obj->operational_status = status;
+ obj->flag_c = c_flag;
+ obj->flag_a = a_flag;
+ obj->flag_r = r_flag;
+ obj->flag_s = s_flag;
+ obj->flag_d = d_flag;
+
+ return obj;
+}
+
+struct pcep_object_vendor_info *
+pcep_obj_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_spec_info)
+{
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)pcep_obj_create_common(
+ sizeof(struct pcep_object_vendor_info),
+ PCEP_OBJ_CLASS_VENDOR_INFO, PCEP_OBJ_TYPE_VENDOR_INFO);
+
+ obj->enterprise_number = enterprise_number;
+ obj->enterprise_specific_info = enterprise_spec_info;
+
+ return obj;
+}
+
+struct pcep_object_inter_layer *
+pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t)
+{
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)pcep_obj_create_common(
+ sizeof(struct pcep_object_inter_layer),
+ PCEP_OBJ_CLASS_INTER_LAYER, PCEP_OBJ_TYPE_INTER_LAYER);
+
+ obj->flag_i = flag_i;
+ obj->flag_m = flag_m;
+ obj->flag_t = flag_t;
+
+ return obj;
+}
+
+struct pcep_object_switch_layer *
+pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows)
+{
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)pcep_obj_create_common(
+ sizeof(struct pcep_object_switch_layer),
+ PCEP_OBJ_CLASS_SWITCH_LAYER,
+ PCEP_OBJ_TYPE_SWITCH_LAYER);
+
+ obj->switch_layer_rows = switch_layer_rows;
+
+ return obj;
+}
+
+struct pcep_object_req_adap_cap *
+pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding)
+{
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)pcep_obj_create_common(
+ sizeof(struct pcep_object_req_adap_cap),
+ PCEP_OBJ_CLASS_REQ_ADAP_CAP,
+ PCEP_OBJ_TYPE_REQ_ADAP_CAP);
+
+ obj->switching_capability = sw_cap;
+ obj->encoding = encoding;
+
+ return obj;
+}
+
+struct pcep_object_server_indication *
+pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)
+ pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_server_indication),
+ PCEP_OBJ_CLASS_SERVER_IND,
+ PCEP_OBJ_TYPE_SERVER_IND, tlv_list);
+
+ obj->switching_capability = sw_cap;
+ obj->encoding = encoding;
+
+ return obj;
+}
+
+struct pcep_object_objective_function *
+pcep_obj_create_objective_function(uint16_t of_code,
+ double_linked_list *tlv_list)
+{
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)
+ pcep_obj_create_common_with_tlvs(
+ sizeof(struct pcep_object_objective_function),
+ PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF, tlv_list);
+
+ obj->of_code = of_code;
+
+ return obj;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list)
+{
+ struct pcep_object_ro *ero =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_ERO,
+ PCEP_OBJ_TYPE_ERO);
+ ero->sub_objects = ero_list;
+
+ return ero;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list)
+{
+ struct pcep_object_ro *iro =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_IRO,
+ PCEP_OBJ_TYPE_IRO);
+ iro->sub_objects = iro_list;
+
+ return iro;
+}
+
+/* Wrap a list of ro subobjects in a structure with an object header */
+struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list)
+{
+ struct pcep_object_ro *rro =
+ (struct pcep_object_ro *)pcep_obj_create_common(
+ sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_RRO,
+ PCEP_OBJ_TYPE_RRO);
+ rro->sub_objects = rro_list;
+
+ return rro;
+}
+
+/*
+ * Route Object Sub-object creation functions
+ */
+
+static struct pcep_object_ro_subobj *
+pcep_obj_create_ro_subobj_common(uint8_t subobj_size,
+ enum pcep_ro_subobj_types ro_subobj_type,
+ bool flag_subobj_loose_hop)
+{
+ struct pcep_object_ro_subobj *ro_subobj =
+ pceplib_malloc(PCEPLIB_MESSAGES, subobj_size);
+ memset(ro_subobj, 0, subobj_size);
+ ro_subobj->flag_subobj_loose_hop = flag_subobj_loose_hop;
+ ro_subobj->ro_subobj_type = ro_subobj_type;
+
+ return ro_subobj;
+}
+
+struct pcep_ro_subobj_ipv4 *
+pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *rro_ipv4,
+ uint8_t prefix_length, bool flag_local_prot)
+{
+ if (rro_ipv4 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_ipv4 *obj =
+ (struct pcep_ro_subobj_ipv4 *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_ipv4), RO_SUBOBJ_TYPE_IPV4,
+ loose_hop);
+ obj->ip_addr.s_addr = rro_ipv4->s_addr;
+ obj->prefix_length = prefix_length;
+ obj->flag_local_protection = flag_local_prot;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_ipv6 *
+pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *rro_ipv6,
+ uint8_t prefix_length, bool flag_local_prot)
+{
+ if (rro_ipv6 == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_ipv6 *obj =
+ (struct pcep_ro_subobj_ipv6 *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_ipv6), RO_SUBOBJ_TYPE_IPV6,
+ loose_hop);
+ obj->prefix_length = prefix_length;
+ obj->flag_local_protection = flag_local_prot;
+ memcpy(&obj->ip_addr, rro_ipv6, sizeof(struct in6_addr));
+
+ return obj;
+}
+
+struct pcep_ro_subobj_unnum *
+pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id)
+{
+ if (router_id == NULL) {
+ return NULL;
+ }
+
+ struct pcep_ro_subobj_unnum *obj =
+ (struct pcep_ro_subobj_unnum *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_unnum),
+ RO_SUBOBJ_TYPE_UNNUM, false);
+ obj->interface_id = if_id;
+ obj->router_id.s_addr = router_id->s_addr;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_32label *
+pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type,
+ uint32_t label)
+{
+ struct pcep_ro_subobj_32label *obj = (struct pcep_ro_subobj_32label *)
+ pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_32label),
+ RO_SUBOBJ_TYPE_LABEL, false);
+ obj->class_type = class_type;
+ obj->flag_global_label = flag_global_label;
+ obj->label = label;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn)
+{
+ struct pcep_ro_subobj_asn *obj =
+ (struct pcep_ro_subobj_asn *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_asn), RO_SUBOBJ_TYPE_ASN,
+ false);
+ obj->asn = asn;
+
+ return obj;
+}
+
+/* Internal util function to create pcep_ro_subobj_sr sub-objects */
+static struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_common(enum pcep_sr_subobj_nai nai_type,
+ bool loose_hop, bool f_flag, bool s_flag,
+ bool c_flag_in, bool m_flag_in)
+{
+ struct pcep_ro_subobj_sr *obj =
+ (struct pcep_ro_subobj_sr *)pcep_obj_create_ro_subobj_common(
+ sizeof(struct pcep_ro_subobj_sr), RO_SUBOBJ_TYPE_SR,
+ loose_hop);
+
+ /* Flag logic according to draft-ietf-pce-segment-routing-16 */
+ bool c_flag = c_flag_in;
+ bool m_flag = m_flag_in;
+ if (s_flag) {
+ c_flag = false;
+ m_flag = false;
+ }
+
+ if (m_flag == false) {
+ c_flag = false;
+ }
+
+ obj->nai_type = nai_type;
+ obj->flag_f = f_flag;
+ obj->flag_s = s_flag;
+ obj->flag_c = c_flag;
+ obj->flag_m = m_flag;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop,
+ uint32_t sid,
+ bool c_flag,
+ bool m_flag)
+{
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=0, the F bit MUST be 1, the S bit MUST be zero and the
+ * Length MUST be 8. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_ABSENT, loose_hop, true, false, c_flag,
+ m_flag);
+ obj->sid = sid;
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *ipv4_node_id)
+{
+ if (ipv4_node_id == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=1, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 8, otherwise the Length MUST be 12 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ /* Since the IP has to be stored in the list, copy it so the caller
+ * doesnt have any restrictions about the type of memory used externally
+ * for the IP. This memory will be freed with the object is freed. */
+ struct in_addr *ipv4_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ ipv4_node_id_copy->s_addr = ipv4_node_id->s_addr;
+ dll_append(obj->nai_list, ipv4_node_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *ipv6_node_id)
+{
+ if (ipv6_node_id == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=2, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 20, otherwise the Length MUST be 24. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *ipv6_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(ipv6_node_id_copy, ipv6_node_id, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, ipv6_node_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *local_ipv4, struct in_addr *remote_ipv4)
+{
+ if (local_ipv4 == NULL || remote_ipv4 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=3, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 12, otherwise the Length MUST be 16 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in_addr *local_ipv4_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ struct in_addr *remote_ipv4_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr));
+ local_ipv4_copy->s_addr = local_ipv4->s_addr;
+ remote_ipv4_copy->s_addr = remote_ipv4->s_addr;
+ dll_append(obj->nai_list, local_ipv4_copy);
+ dll_append(obj->nai_list, remote_ipv4_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6)
+{
+ if (local_ipv6 == NULL || remote_ipv6 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=4, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 36, otherwise the Length MUST be 40 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, loose_hop, false, sid_absent,
+ c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *local_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ struct in6_addr *remote_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr));
+ memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, local_ipv6_copy);
+ dll_append(obj->nai_list, remote_ipv6_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id,
+ uint32_t remote_if_id)
+{
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=5, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 20, otherwise the Length MUST be 24. */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, loose_hop, false,
+ sid_absent, c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+
+ obj->nai_list = dll_initialize();
+ uint32_t *local_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_node_id_copy = local_node_id;
+ dll_append(obj->nai_list, local_node_id_copy);
+
+ uint32_t *local_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_if_id_copy = local_if_id;
+ dll_append(obj->nai_list, local_if_id_copy);
+
+ uint32_t *remote_node_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_node_id_copy = remote_node_id;
+ dll_append(obj->nai_list, remote_node_id_copy);
+
+ uint32_t *remote_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_if_id_copy = remote_if_id;
+ dll_append(obj->nai_list, remote_if_id_copy);
+
+ return obj;
+}
+
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, uint32_t local_if_id,
+ struct in6_addr *remote_ipv6, uint32_t remote_if_id)
+{
+ if (local_ipv6 == NULL || remote_ipv6 == NULL) {
+ return NULL;
+ }
+
+ /* According to draft-ietf-pce-segment-routing-16#section-5.2.1
+ * If NT=6, the F bit MUST be zero. If the S bit is 1, the Length
+ * MUST be 44, otherwise the Length MUST be 48 */
+ struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common(
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, loose_hop, false,
+ sid_absent, c_flag, m_flag);
+
+ if (!sid_absent) {
+ obj->sid = sid;
+ }
+ obj->nai_list = dll_initialize();
+ struct in6_addr *local_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, local_ipv6_copy);
+
+ uint32_t *local_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *local_if_id_copy = local_if_id;
+ dll_append(obj->nai_list, local_if_id_copy);
+
+ struct in6_addr *remote_ipv6_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr));
+ memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr));
+ dll_append(obj->nai_list, remote_ipv6_copy);
+
+ uint32_t *remote_if_id_copy =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *remote_if_id_copy = remote_if_id;
+ dll_append(obj->nai_list, remote_if_id_copy);
+
+ return obj;
+}
diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h
new file mode 100644
index 0000000000..959a6f8cf6
--- /dev/null
+++ b/pceplib/pcep_msg_objects.h
@@ -0,0 +1,741 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message object API.
+ */
+
+#ifndef PCEP_OBJECTS_H
+#define PCEP_OBJECTS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "pcep.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_tlvs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Regarding memory usage:
+ * When creating objects, any objects passed into these APIs will be free'd when
+ * the enclosing pcep_message is free'd. That includes the double_linked_list's.
+ * So, just create the objects and TLVs, put them in their double_linked_list's,
+ * and everything will be managed internally. The enclosing message will be
+ * deleted by pcep_msg_free_message() or pcep_msg_free_message_list() which,
+ * in turn will call one of: pcep_obj_free_object() and pcep_obj_free_tlv().
+ * For received messages with objects, call pcep_msg_free_message() to free
+ * them.
+ */
+
+enum pcep_object_classes {
+ PCEP_OBJ_CLASS_OPEN = 1,
+ PCEP_OBJ_CLASS_RP = 2,
+ PCEP_OBJ_CLASS_NOPATH = 3,
+ PCEP_OBJ_CLASS_ENDPOINTS = 4,
+ PCEP_OBJ_CLASS_BANDWIDTH = 5,
+ PCEP_OBJ_CLASS_METRIC = 6,
+ PCEP_OBJ_CLASS_ERO = 7,
+ PCEP_OBJ_CLASS_RRO = 8,
+ PCEP_OBJ_CLASS_LSPA = 9,
+ PCEP_OBJ_CLASS_IRO = 10,
+ PCEP_OBJ_CLASS_SVEC = 11,
+ PCEP_OBJ_CLASS_NOTF = 12,
+ PCEP_OBJ_CLASS_ERROR = 13,
+ PCEP_OBJ_CLASS_CLOSE = 15,
+ PCEP_OBJ_CLASS_OF = 21,
+ PCEP_OBJ_CLASS_LSP = 32,
+ PCEP_OBJ_CLASS_SRP = 33,
+ PCEP_OBJ_CLASS_VENDOR_INFO = 34,
+ PCEP_OBJ_CLASS_INTER_LAYER = 36, /* RFC 8282 */
+ PCEP_OBJ_CLASS_SWITCH_LAYER = 37, /* RFC 8282 */
+ PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, /* RFC 8282 */
+ PCEP_OBJ_CLASS_SERVER_IND = 39, /* RFC 8282 */
+ PCEP_OBJ_CLASS_ASSOCIATION = 40, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_CLASS_MAX,
+};
+
+enum pcep_object_types {
+ PCEP_OBJ_TYPE_OPEN = 1,
+ PCEP_OBJ_TYPE_RP = 1,
+ PCEP_OBJ_TYPE_NOPATH = 1,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4 = 1,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6 = 2,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ = 1,
+ PCEP_OBJ_TYPE_BANDWIDTH_TELSP = 2,
+ PCEP_OBJ_TYPE_BANDWIDTH_CISCO =
+ 5, /* IANA unassigned, but rcvd from Cisco PCE */
+ PCEP_OBJ_TYPE_SRP = 1,
+ PCEP_OBJ_TYPE_VENDOR_INFO = 1,
+ PCEP_OBJ_TYPE_LSP = 1,
+ PCEP_OBJ_TYPE_METRIC = 1,
+ PCEP_OBJ_TYPE_ERO = 1,
+ PCEP_OBJ_TYPE_RRO = 1,
+ PCEP_OBJ_TYPE_LSPA = 1,
+ PCEP_OBJ_TYPE_IRO = 1,
+ PCEP_OBJ_TYPE_SVEC = 1,
+ PCEP_OBJ_TYPE_NOTF = 1,
+ PCEP_OBJ_TYPE_ERROR = 1,
+ PCEP_OBJ_TYPE_CLOSE = 1,
+ PCEP_OBJ_TYPE_INTER_LAYER = 1,
+ PCEP_OBJ_TYPE_SWITCH_LAYER = 1,
+ PCEP_OBJ_TYPE_REQ_ADAP_CAP = 1,
+ PCEP_OBJ_TYPE_SERVER_IND = 1,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4 =
+ 1, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6 =
+ 2, /*draft-ietf-pce-association-group-10*/
+ PCEP_OBJ_TYPE_OF = 1,
+ PCEP_OBJ_TYPE_MAX = 2,
+};
+
+#define OBJECT_HEADER_FLAG_I 0x01
+#define OBJECT_HEADER_FLAG_P 0x02
+
+/* The flag_p and flag_i arent set via the APIs, if they need to be set, just
+ * set them on the returned object once it has been created. */
+struct pcep_object_header {
+ enum pcep_object_classes object_class;
+ enum pcep_object_types object_type;
+ bool flag_p; /* PCC Processing rule bit: When set, the object MUST be
+ taken into account, when cleared the object is optional.
+ */
+ bool flag_i; /* PCE Ignore bit: indicates to a PCC whether or not an
+ optional object was processed */
+ double_linked_list *tlv_list;
+ /* Pointer into encoded_message field from the pcep_message */
+ const uint8_t *encoded_object;
+ uint16_t encoded_object_length;
+};
+
+#define PCEP_OBJECT_OPEN_VERSION 1
+
+struct pcep_object_open {
+ struct pcep_object_header header;
+ uint8_t open_version; /* PCEP version. Current version is 1 */
+ uint8_t open_keepalive; /* Maximum period of time between two
+ consecutive PCEP messages sent by the sender.
+ */
+ uint8_t open_deadtimer; /* Specifies the amount of time before closing
+ the session down. */
+ uint8_t open_sid; /* PCEP session number that identifies the current
+ session. */
+};
+
+#define OBJECT_RP_FLAG_R 0x08
+#define OBJECT_RP_FLAG_B 0x10
+#define OBJECT_RP_FLAG_O 0x20
+#define OBJECT_RP_FLAG_OF 0x80
+#define OBJECT_RP_MAX_PRIORITY 0x07
+
+struct pcep_object_rp {
+ struct pcep_object_header header;
+ uint8_t priority; /* 3 bit priority, max priority is 7 */
+ bool flag_reoptimization;
+ bool flag_bidirectional;
+ bool flag_strict; /* when set, a loose path is acceptable */
+ bool flag_of; /* Supply Objective Function on Response */
+ uint32_t request_id; /* The Request-id-number value combined with the
+ source for PCC & PCE creates a uniquely number.
+ */
+};
+
+enum pcep_notification_types {
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED = 1,
+ PCEP_NOTIFY_TYPE_PCE_OVERLOADED = 2
+};
+
+enum pcep_notification_values {
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST = 1,
+ PCEP_NOTIFY_VALUE_PCE_CANCELLED_REQUEST = 2,
+ PCEP_NOTIFY_VALUE_PCE_CURRENTLY_OVERLOADED = 1,
+ PCEP_NOTIFY_VALUE_PCE_NO_LONGER_OVERLOADED = 2
+};
+
+struct pcep_object_notify {
+ struct pcep_object_header header;
+ enum pcep_notification_types notification_type;
+ enum pcep_notification_values notification_value;
+};
+
+enum pcep_association_type {
+ PCEP_ASSOCIATION_TYPE_PATH_PROTECTION_ASSOCIATION =
+ 1, // iana unique value define as 2020-01-08!
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE =
+ 65535 // TBD1 draft-barth-pce-segment-routing-policy-cp-04
+};
+#define OBJECT_ASSOCIATION_FLAG_R 0x01
+struct pcep_object_association_ipv4 { // draft-ietf-pce-association-group-10
+ struct pcep_object_header header;
+ bool R_flag;
+ uint16_t association_type;
+ uint16_t association_id;
+ struct in_addr src;
+};
+
+struct pcep_object_association_ipv6 { // draft-ietf-pce-association-group-10
+ struct pcep_object_header header;
+ bool R_flag;
+ uint16_t association_type;
+ uint16_t association_id;
+ struct in6_addr src;
+};
+
+
+enum pcep_nopath_nature_of_issue {
+ PCEP_NOPATH_NI_NO_PATH_FOUND = 0,
+ PCEP_NOPATH_NI_PCE_CHAIN_BROKEN = 1,
+};
+
+enum pcep_nopath_tlv_err_codes {
+ PCEP_NOPATH_TLV_ERR_NO_TLV = 0,
+ PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE = 1,
+ PCEP_NOPATH_TLV_ERR_UNKNOWN_DST = 2,
+ PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC = 3
+};
+
+#define OBJECT_NOPATH_FLAG_C 0x80
+
+struct pcep_object_nopath {
+ struct pcep_object_header header;
+ uint8_t ni; /* Nature of Issue, reports the nature of the issue that led
+ to a negative reply */
+ bool flag_c; /* when set, indicates the unsatisfied constraints by
+ including relevant PCEP objects. */
+ enum pcep_nopath_tlv_err_codes
+ err_code; /* When set other than 0, an appropriate TLV will be
+ included */
+};
+
+struct pcep_object_endpoints_ipv4 {
+ struct pcep_object_header header;
+ struct in_addr src_ipv4;
+ struct in_addr dst_ipv4;
+};
+
+struct pcep_object_endpoints_ipv6 {
+ struct pcep_object_header header;
+ struct in6_addr src_ipv6;
+ struct in6_addr dst_ipv6;
+};
+
+/* PCEP floats are encoded according to:
+ * https://en.wikipedia.org/wiki/IEEE_754-1985
+ * Luckily, this is the same encoding used by C */
+struct pcep_object_bandwidth {
+ struct pcep_object_header header;
+ float bandwidth;
+};
+
+enum pcep_metric_types {
+ /* RFC 5440 */
+ PCEP_METRIC_IGP = 1,
+ PCEP_METRIC_TE = 2,
+ PCEP_METRIC_HOP_COUNT = 3,
+ /* RFC 5541 */
+ PCEP_METRIC_AGGREGATE_BW = 4,
+ PCEP_METRIC_MOST_LOADED_LINK = 5,
+ PCEP_METRIC_CUMULATIVE_IGP = 6,
+ PCEP_METRIC_CUMULATIVE_TE = 7,
+ /* RFC 8306 */
+ PCEP_METRIC_P2MP_IGP = 8,
+ PCEP_METRIC_P2MP_TE = 9,
+ PCEP_METRIC_P2MP_HOP_COUNT = 10,
+ /* RFC 8864 */
+ PCEP_METRIC_SEGMENT_ID_DEPTH = 11,
+ /* RFC 8233 */
+ PCEP_METRIC_PATH_DELAY = 12,
+ PCEP_METRIC_PATH_DELAY_VARIATION = 13,
+ PCEP_METRIC_PATH_LOSS = 14,
+ PCEP_METRIC_P2MP_PATH_DELAY = 15,
+ PCEP_METRIC_P2MP_PATH_DELAY_VARIATION = 16,
+ PCEP_METRIC_P2MP_PATH_LOSS = 17,
+ /* RFC 8282 */
+ PCEP_METRIC_NUM_PATH_ADAPTATIONS = 18,
+ PCEP_METRIC_NUM_PATH_LAYERS = 19,
+ /* RFC 8685 */
+ PCEP_METRIC_DOMAIN_COUNT = 20,
+ PCEP_METRIC_BORDER_NODE_COUNT = 21,
+};
+
+#define OBJECT_METRIC_FLAC_B 0x01
+#define OBJECT_METRIC_FLAC_C 0x02
+
+/* PCEP floats are encoded according to:
+ * https://en.wikipedia.org/wiki/IEEE_754-1985
+ * Luckily, this is the same encoding used by C */
+struct pcep_object_metric {
+ struct pcep_object_header header;
+ enum pcep_metric_types type;
+ bool flag_b; /* Bound flag */
+ bool flag_c; /* Computed metric */
+ float value; /* Metric value in 32 bits */
+};
+
+#define OBJECT_LSPA_FLAG_L 0x01
+
+struct pcep_object_lspa {
+ struct pcep_object_header header;
+ uint32_t lspa_exclude_any;
+ uint32_t lspa_include_any;
+ uint32_t lspa_include_all;
+ uint8_t setup_priority;
+ uint8_t holding_priority;
+ bool flag_local_protection; /* Local protection desired bit */
+};
+
+/* The SVEC object with some custom extensions. */
+#define OBJECT_SVEC_FLAG_L 0x01
+#define OBJECT_SVEC_FLAG_N 0x02
+#define OBJECT_SVEC_FLAG_S 0x04
+
+struct pcep_object_svec {
+ struct pcep_object_header header;
+ bool flag_link_diverse;
+ bool flag_node_diverse;
+ bool flag_srlg_diverse;
+ double_linked_list
+ *request_id_list; /* list of 32-bit request ID pointers */
+};
+
+struct pcep_object_error {
+ struct pcep_object_header header;
+ enum pcep_error_type error_type;
+ enum pcep_error_value error_value;
+};
+
+struct pcep_object_load_balancing {
+ struct pcep_object_header header;
+ uint8_t load_maxlsp; /* Maximum number of TE LSPs in the set */
+ uint32_t load_minband; /* Specifies the minimum bandwidth of each
+ element */
+};
+
+enum pcep_close_reason {
+ PCEP_CLOSE_REASON_NO = 1,
+ PCEP_CLOSE_REASON_DEADTIMER = 2,
+ PCEP_CLOSE_REASON_FORMAT = 3,
+ PCEP_CLOSE_REASON_UNKNOWN_REQ = 4,
+ PCEP_CLOSE_REASON_UNREC_MSG = 5
+};
+
+struct pcep_object_close {
+ struct pcep_object_header header;
+ enum pcep_close_reason reason;
+};
+
+/* Stateful PCE Request Parameters RFC 8231, 8281 */
+
+#define OBJECT_SRP_FLAG_R 0x01
+
+struct pcep_object_srp {
+ struct pcep_object_header header;
+ bool flag_lsp_remove; /* RFC 8281 */
+ uint32_t srp_id_number;
+};
+
+/* Label Switched Path Object RFC 8231 */
+enum pcep_lsp_operational_status {
+ PCEP_LSP_OPERATIONAL_DOWN = 0,
+ PCEP_LSP_OPERATIONAL_UP = 1,
+ PCEP_LSP_OPERATIONAL_ACTIVE = 2,
+ PCEP_LSP_OPERATIONAL_GOING_DOWN = 3,
+ PCEP_LSP_OPERATIONAL_GOING_UP = 4,
+};
+
+#define MAX_PLSP_ID 0x000fffff /* The plsp_id is only 20 bits */
+#define MAX_LSP_STATUS 0x0007 /* The status is only 3 bits */
+#define OBJECT_LSP_FLAG_D 0x01
+#define OBJECT_LSP_FLAG_S 0x02
+#define OBJECT_LSP_FLAG_R 0x04
+#define OBJECT_LSP_FLAG_A 0x08
+#define OBJECT_LSP_FLAG_C 0x80
+
+struct pcep_object_lsp {
+ struct pcep_object_header header;
+ uint32_t plsp_id; /* plsp_id is 20 bits, must be <= MAX_PLSP_ID*/
+ enum pcep_lsp_operational_status operational_status; /* max 3 bits */
+ bool flag_d;
+ bool flag_s;
+ bool flag_r;
+ bool flag_a;
+ bool flag_c;
+};
+
+/* RFC 7470 */
+struct pcep_object_vendor_info {
+ struct pcep_object_header header;
+ uint32_t enterprise_number;
+ uint32_t enterprise_specific_info;
+};
+
+/* RFC 8282 */
+#define OBJECT_INTER_LAYER_FLAG_I 0x01
+#define OBJECT_INTER_LAYER_FLAG_M 0x02
+#define OBJECT_INTER_LAYER_FLAG_T 0x04
+
+struct pcep_object_inter_layer {
+ struct pcep_object_header header;
+ bool flag_i;
+ bool flag_m;
+ bool flag_t;
+};
+
+/* RFC 8282 */
+#define OBJECT_SWITCH_LAYER_FLAG_I 0x01
+enum pcep_lsp_encoding_type {
+ /* Values taken from RFC 3471 as suggested by RFC 8282 */
+ PCEP_LSP_ENC_PACKET = 1,
+ PCEP_LSP_ENC_ETHERNET = 2,
+ PCEP_LSP_ENC_PDH = 3,
+ PCEP_LSP_ENC_RESERVED4 = 4,
+ PCEP_LSP_ENC_SDH_SONET = 5,
+ PCEP_LSP_ENC_RESERVED6 = 6,
+ PCEP_LSP_ENC_DIG_WRAPPER = 7,
+ PCEP_LSP_ENC_LAMBDA = 8,
+ PCEP_LSP_ENC_FIBER = 9,
+ PCEP_LSP_ENC_RESERVED10 = 10,
+ PCEP_LSP_ENC_FIBER_CHAN = 11
+};
+
+enum pcep_switching_capability {
+ /* Switching capability values taken from RFC 4203/3471 as suggested by
+ RFC 8282 */
+ PCEP_SW_CAP_PSC1 = 1, /* Packet-Switch Capable-1 (PSC-1) */
+ PCEP_SW_CAP_PSC2 = 2,
+ PCEP_SW_CAP_PSC3 = 3,
+ PCEP_SW_CAP_PSC4 = 4,
+ PCEP_SW_CAP_L2SC = 51, /* Layer-2 Switch Capable */
+ PCEP_SW_CAP_TDM = 100, /* Time-Division-Multiplex Capable */
+ PCEP_SW_CAP_LSC = 150, /* Lambda-Switch Capable */
+ PCEP_SW_CAP_FSC = 200 /* Fiber-Switch Capable */
+};
+
+struct pcep_object_switch_layer_row {
+ enum pcep_lsp_encoding_type lsp_encoding_type;
+ enum pcep_switching_capability switching_type;
+ bool flag_i;
+};
+
+struct pcep_object_switch_layer {
+ struct pcep_object_header header;
+ double_linked_list
+ *switch_layer_rows; /* list of struct
+ pcep_object_switch_layer_row */
+};
+
+/* RFC 8282
+ * Requested Adaptation capability */
+
+struct pcep_object_req_adap_cap {
+ struct pcep_object_header header;
+ enum pcep_switching_capability switching_capability;
+ enum pcep_lsp_encoding_type encoding;
+};
+
+/* RFC 8282 */
+
+struct pcep_object_server_indication {
+ struct pcep_object_header header;
+ enum pcep_switching_capability switching_capability;
+ enum pcep_lsp_encoding_type encoding;
+ /* This object is identical to req_adap_cap, except it allows TLVs */
+};
+
+/* Objective Function Object: RFC 5541 */
+
+struct pcep_object_objective_function {
+ struct pcep_object_header header;
+ uint16_t of_code;
+};
+
+/*
+ * Common Route Object sub-object definitions
+ * used by ERO, IRO, and RRO
+ */
+
+/* Common Route Object sub-object types
+ * used by ERO, IRO, and RRO */
+enum pcep_ro_subobj_types {
+ RO_SUBOBJ_TYPE_IPV4 = 1, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_IPV6 = 2, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_LABEL = 3, /* RFC 3209 */
+ RO_SUBOBJ_TYPE_UNNUM = 4, /* RFC 3477 */
+ RO_SUBOBJ_TYPE_ASN = 32, /* RFC 3209, Section 4.3.3.4 */
+ RO_SUBOBJ_TYPE_SR = 36, /* RFC 8408, draft-ietf-pce-segment-routing-16.
+ Type 5 for draft07 has been assigned to
+ something else. */
+ RO_SUBOBJ_UNKNOWN
+};
+
+struct pcep_object_ro {
+ struct pcep_object_header header;
+ double_linked_list
+ *sub_objects; /* list of struct pcep_object_ro_subobj */
+};
+
+struct pcep_object_ro_subobj {
+ bool flag_subobj_loose_hop; /* L subobj flag */
+ enum pcep_ro_subobj_types ro_subobj_type;
+};
+
+#define OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT 0x01
+
+struct pcep_ro_subobj_ipv4 {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in_addr ip_addr;
+ uint8_t prefix_length;
+ bool flag_local_protection;
+};
+
+struct pcep_ro_subobj_ipv6 {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in6_addr ip_addr;
+ uint8_t prefix_length;
+ bool flag_local_protection;
+};
+
+struct pcep_ro_subobj_unnum {
+ struct pcep_object_ro_subobj ro_subobj;
+ struct in_addr router_id;
+ uint32_t interface_id;
+};
+
+#define OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL 0x01
+struct pcep_ro_subobj_32label {
+ struct pcep_object_ro_subobj ro_subobj;
+ bool flag_global_label;
+ uint8_t class_type; /* label class-type (generalized label = 2) */
+ uint32_t label; /* label supported */
+};
+
+struct pcep_ro_subobj_asn {
+ struct pcep_object_ro_subobj ro_subobj;
+ uint16_t asn; /* Autonomous system number */
+};
+
+/* The SR ERO and SR RRO subojbects are the same, except
+ * the SR-RRO does not have the L flag in the Type field.
+ * Defined in draft-ietf-pce-segment-routing-16 */
+enum pcep_sr_subobj_nai {
+ PCEP_SR_SUBOBJ_NAI_ABSENT = 0,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE = 1,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE = 2,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY = 3,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY = 4,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY = 5,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY = 6,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN
+};
+
+#define OBJECT_SUBOBJ_SR_FLAG_M 0x01
+#define OBJECT_SUBOBJ_SR_FLAG_C 0x02
+#define OBJECT_SUBOBJ_SR_FLAG_S 0x04
+#define OBJECT_SUBOBJ_SR_FLAG_F 0x08
+
+struct pcep_ro_subobj_sr {
+ struct pcep_object_ro_subobj ro_subobj;
+ enum pcep_sr_subobj_nai nai_type;
+ bool flag_f;
+ bool flag_s;
+ bool flag_c;
+ bool flag_m;
+
+ /* The SID and NAI are optional depending on the flags,
+ * and the NAI can be variable length */
+ uint32_t sid;
+ double_linked_list
+ *nai_list; /* double linked list of in_addr or in6_addr */
+};
+
+/* Macros to make a SID Label
+ *
+ * 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Label
+ | Label | TC |S| TTL | Stack
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Entry
+ */
+#define ENCODE_SR_ERO_SID(label_20bits, tc_3bits, stack_bottom_bit, ttl_8bits) \
+ ((((label_20bits) << 12) & 0xfffff000) \
+ | (((tc_3bits) << 9) & 0x00000e00) \
+ | (((stack_bottom_bit) << 8) & 0x00000100) | ((ttl_8bits)&0xff))
+#define GET_SR_ERO_SID_LABEL(SID) ((SID & 0xfffff000) >> 12)
+#define GET_SR_ERO_SID_TC(SID) ((SID & 0x00000e00) >> 9)
+#define GET_SR_ERO_SID_S(SID) ((SID & 0x00000100) >> 8)
+#define GET_SR_ERO_SID_TTL(SID) ((SID & 0x000000ff))
+
+/*
+ * All created objects will be in Host byte order, except for IPs.
+ * All IP addresses are expected to be passed-in in Network byte order,
+ * and any objects received will have their IPs in Network byte order.
+ * The message containing the objects should be converted to Network byte order
+ * with pcep_encode_msg_header() before sending, which will also convert the
+ * Objects, TLVs, and sub-objects.
+ */
+
+struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive,
+ uint8_t deadtimer, uint8_t sid,
+ double_linked_list *tlv_list);
+struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r,
+ bool flag_b, bool flag_s,
+ bool flag_of, uint32_t reqid,
+ double_linked_list *tlv_list);
+struct pcep_object_notify *
+pcep_obj_create_notify(enum pcep_notification_types notification_type,
+ enum pcep_notification_values notification_value);
+struct pcep_object_nopath *
+pcep_obj_create_nopath(uint8_t ni, bool flag_c,
+ enum pcep_nopath_tlv_err_codes error_code);
+struct pcep_object_association_ipv4 *
+pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in_addr src);
+struct pcep_object_association_ipv6 *
+pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type,
+ uint16_t association_id, struct in6_addr src);
+struct pcep_object_endpoints_ipv4 *
+pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4,
+ const struct in_addr *dst_ipv4);
+struct pcep_object_endpoints_ipv6 *
+pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6,
+ const struct in6_addr *dst_ipv6);
+struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth);
+struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type,
+ bool flag_b, bool flag_c,
+ float value);
+struct pcep_object_lspa *
+pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any,
+ uint32_t include_all, uint8_t setup_priority,
+ uint8_t holding_priority, bool flag_local_protection);
+struct pcep_object_svec *
+pcep_obj_create_svec(bool srlg, bool node, bool link,
+ double_linked_list *request_id_list);
+struct pcep_object_error *
+pcep_obj_create_error(enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason);
+struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove,
+ uint32_t srp_id_number,
+ double_linked_list *tlv_list);
+struct pcep_object_lsp *
+pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status,
+ bool c_flag, bool a_flag, bool r_flag, bool s_flag,
+ bool d_flag, double_linked_list *tlv_list);
+struct pcep_object_vendor_info *
+pcep_obj_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_spec_info);
+struct pcep_object_inter_layer *
+pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t);
+struct pcep_object_switch_layer *
+pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows);
+struct pcep_object_req_adap_cap *
+pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding);
+struct pcep_object_server_indication *
+pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap,
+ enum pcep_lsp_encoding_type encoding,
+ double_linked_list *tlv_list);
+struct pcep_object_objective_function *
+pcep_obj_create_objective_function(uint16_t of_code,
+ double_linked_list *tlv_list);
+
+/* Route Object (Explicit ero, Reported rro, and Include iro) functions
+ * First, the sub-objects should be created and appended to a
+ * double_linked_list, then call one of these Route Object creation functions
+ * with the subobj list */
+struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list);
+struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list);
+struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list);
+/* Route Object sub-object creation functions */
+struct pcep_ro_subobj_ipv4 *
+pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *ro_ipv4,
+ uint8_t prefix_len, bool flag_local_prot);
+struct pcep_ro_subobj_ipv6 *
+pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *ro_ipv6,
+ uint8_t prefix_len, bool flag_local_prot);
+struct pcep_ro_subobj_unnum *
+pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id);
+struct pcep_ro_subobj_32label *
+pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type,
+ uint32_t label);
+struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn);
+
+/* SR ERO and SR RRO creation functions for different NAI (Node/Adj ID) types.
+ * - The loose_hop is only used for sr ero and must always be false for sr rro.
+ * - The NAI value will be set internally, depending on which function is used.
+ * m_flag:
+ * - If this flag is true, the SID value represents an MPLS label stack
+ * entry as specified in [RFC3032]. Otherwise, the SID value is an
+ * administratively configured value which represents an index into
+ * an MPLS label space (either SRGB or SRLB) per [RFC8402].
+ * c_flag:
+ * - If the M flag and the C flag are both true, then the TC, S, and TTL
+ * fields in the MPLS label stack entry are specified by the PCE. However,
+ * a PCC MAY choose to override these values according to its local policy
+ * and MPLS forwarding rules.
+ * - If the M flag is true but the C flag is false, then the TC, S, and TTL
+ * fields MUST be ignored by the PCC.
+ * - The PCC MUST set these fields according to its local policy and MPLS
+ * forwarding rules.
+ * - If the M flag is false then the C bit MUST be false. */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop,
+ uint32_t sid,
+ bool c_flag,
+ bool m_flag);
+
+/* The ipv4_node_id will be copied internally */
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *ipv4_node_id);
+/* The ipv6_node_id will be copied internally */
+struct pcep_ro_subobj_sr *
+pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent,
+ bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *ipv6_node_id);
+/* The local_ipv4 and remote_ipv4 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in_addr *local_ipv4, struct in_addr *remote_ipv4);
+/* The local_ipv6 and remote_ipv6 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6);
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id,
+ uint32_t remote_if_id);
+/* The local_ipv6 and remote_ipv6 will be copied internally */
+struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid,
+ struct in6_addr *local_ipv6, uint32_t local_if_id,
+ struct in6_addr *remote_ipv6, uint32_t remote_if_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_msg_objects_encoding.c b/pceplib/pcep_msg_objects_encoding.c
new file mode 100644
index 0000000000..d40b840869
--- /dev/null
+++ b/pceplib/pcep_msg_objects_encoding.c
@@ -0,0 +1,1720 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP Objects.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep_msg_objects.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_object_header(struct pcep_object_header *object_hdr,
+ uint16_t object_length, uint8_t *buf);
+void pcep_decode_object_hdr(const uint8_t *obj_buf,
+ struct pcep_object_header *obj_hdr);
+void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l,
+ uint8_t subobj_type);
+
+/*
+ * forward declarations for initialize_object_encoders()
+ */
+uint16_t pcep_encode_obj_open(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_rp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_nopath(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_association(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_metric(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_ro(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_lspa(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_svec(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_notify(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_error(struct pcep_object_header *error,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_close(struct pcep_object_header *close,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_srp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_lsp(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning, uint8_t *buf);
+uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *obj,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+typedef uint16_t (*object_encoder_funcptr)(struct pcep_object_header *,
+ struct pcep_versioning *versioning,
+ uint8_t *buf);
+
+#define MAX_OBJECT_ENCODER_INDEX 64
+
+#define PCEP_ENCODERS_ARGS \
+ struct pcep_object_header *, struct pcep_versioning *versioning, \
+ uint8_t *buf
+uint16_t (*const object_encoders[MAX_OBJECT_ENCODER_INDEX])(
+ PCEP_ENCODERS_ARGS) = {
+ [PCEP_OBJ_CLASS_OPEN] = pcep_encode_obj_open,
+ [PCEP_OBJ_CLASS_RP] = pcep_encode_obj_rp,
+ [PCEP_OBJ_CLASS_NOPATH] = pcep_encode_obj_nopath,
+ [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_encode_obj_endpoints,
+ [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_encode_obj_bandwidth,
+ [PCEP_OBJ_CLASS_METRIC] = pcep_encode_obj_metric,
+ [PCEP_OBJ_CLASS_ERO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_RRO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_LSPA] = pcep_encode_obj_lspa,
+ [PCEP_OBJ_CLASS_IRO] = pcep_encode_obj_ro,
+ [PCEP_OBJ_CLASS_SVEC] = pcep_encode_obj_svec,
+ [PCEP_OBJ_CLASS_NOTF] = pcep_encode_obj_notify,
+ [PCEP_OBJ_CLASS_ERROR] = pcep_encode_obj_error,
+ [PCEP_OBJ_CLASS_CLOSE] = pcep_encode_obj_close,
+ [PCEP_OBJ_CLASS_LSP] = pcep_encode_obj_lsp,
+ [PCEP_OBJ_CLASS_SRP] = pcep_encode_obj_srp,
+ [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_encode_obj_association,
+ [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_encode_obj_inter_layer,
+ [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_encode_obj_switch_layer,
+ [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_encode_obj_req_adap_cap,
+ [PCEP_OBJ_CLASS_SERVER_IND] = pcep_encode_obj_server_ind,
+ [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_encode_obj_vendor_info,
+ [PCEP_OBJ_CLASS_OF] = pcep_encode_obj_objective_function,
+};
+/*
+ * forward declarations for initialize_object_decoders()
+ */
+struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_endpoints(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_association(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_bandwidth(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_vendor_info(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_inter_layer(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_switch_layer(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_server_ind(struct pcep_object_header *hdr, const uint8_t *buf);
+struct pcep_object_header *
+pcep_decode_obj_objective_function(struct pcep_object_header *hdr,
+ const uint8_t *buf);
+typedef struct pcep_object_header *(*object_decoder_funcptr)(
+ struct pcep_object_header *, const uint8_t *buf);
+
+#define PCEP_DECODERS_ARGS struct pcep_object_header *, const uint8_t *buf
+
+struct pcep_object_header *(*const object_decoders[MAX_OBJECT_ENCODER_INDEX])(
+ PCEP_DECODERS_ARGS) = {
+ [PCEP_OBJ_CLASS_OPEN] = pcep_decode_obj_open,
+ [PCEP_OBJ_CLASS_RP] = pcep_decode_obj_rp,
+ [PCEP_OBJ_CLASS_NOPATH] = pcep_decode_obj_nopath,
+ [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_decode_obj_endpoints,
+ [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_decode_obj_bandwidth,
+ [PCEP_OBJ_CLASS_METRIC] = pcep_decode_obj_metric,
+ [PCEP_OBJ_CLASS_ERO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_RRO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_LSPA] = pcep_decode_obj_lspa,
+ [PCEP_OBJ_CLASS_IRO] = pcep_decode_obj_ro,
+ [PCEP_OBJ_CLASS_SVEC] = pcep_decode_obj_svec,
+ [PCEP_OBJ_CLASS_NOTF] = pcep_decode_obj_notify,
+ [PCEP_OBJ_CLASS_ERROR] = pcep_decode_obj_error,
+ [PCEP_OBJ_CLASS_CLOSE] = pcep_decode_obj_close,
+ [PCEP_OBJ_CLASS_LSP] = pcep_decode_obj_lsp,
+ [PCEP_OBJ_CLASS_SRP] = pcep_decode_obj_srp,
+ [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_decode_obj_association,
+ [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_decode_obj_inter_layer,
+ [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_decode_obj_switch_layer,
+ [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_decode_obj_req_adap_cap,
+ [PCEP_OBJ_CLASS_SERVER_IND] = pcep_decode_obj_server_ind,
+ [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_decode_obj_vendor_info,
+ [PCEP_OBJ_CLASS_OF] = pcep_decode_obj_objective_function,
+};
+
+/* Object lengths, including the Object Header.
+ * Used by pcep_object_get_length() and pcep_object_has_tlvs() */
+static uint8_t pcep_object_class_lengths[] = {
+ 0, /* Object class 0 unused */
+ 8, /* PCEP_OBJ_CLASS_OPEN = 1 */
+ 12, /* PCEP_OBJ_CLASS_RP = 2 */
+ 16, /* PCEP_OBJ_CLASS_NOPATH = 3, includes 8 for mandatory TLV */
+ 0, /* PCEP_OBJ_CLASS_ENDPOINTS = 4, could be ipv4 or ipv6, setting to 0
+ */
+ 8, /* PCEP_OBJ_CLASS_BANDWIDTH = 5 */
+ 12, /* PCEP_OBJ_CLASS_METRIC = 6 */
+ 0, /* PCEP_OBJ_CLASS_ERO = 7, setting 0, ROs cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_RRO = 8, setting 0, ROs cannot have TLVs */
+ 20, /* PCEP_OBJ_CLASS_LSPA = 9 */
+ 0, /* PCEP_OBJ_CLASS_IRO = 10, setting 0, ROs cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_SVEC = 11, SVECs cannot have TLVs */
+ 8, /* PCEP_OBJ_CLASS_NOTF = 12 */
+ 8, /* PCEP_OBJ_CLASS_ERROR = 13 */
+ 0, /* Object class 14 unused */
+ 8, /* PCEP_OBJ_CLASS_CLOSE = 15 */
+ 0, 0, 0, 0, 0, /* Object classes 16 - 20 are not used */
+ 8, /* PCEP_OBJ_CLASS_OF = 21 */
+ 0, 0, 0, 0, 0, /* Object classes 22 - 26 are not used */
+ 0, 0, 0, 0, 0, /* Object classes 27 - 31 are not used */
+ 8, /* PCEP_OBJ_CLASS_LSP = 32 */
+ 12, /* PCEP_OBJ_CLASS_SRP = 33 */
+ 12, /* PCEP_OBJ_CLASS_VENDOR_INFO = 34 */
+ 0, /* Object class 35 unused */
+ 0, /* PCEP_OBJ_CLASS_INTER_LAYER = 36, cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_SWITCH_LAYER = 37, cannot have TLVs */
+ 0, /* PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, cannot have TLVs*/
+ 8, /* PCEP_OBJ_CLASS_SERVER_IND = 39 */
+ 0, /* PCEP_OBJ_CLASS_ASSOCIATION = 40, cannot have TLVs */
+};
+
+/*
+ * The TLVs can have strange length values, since they do not include padding in
+ * the TLV header length, but that extra padding must be taken into account by
+ * the enclosing object by rounding up to the next 4 byte boundary.
+ * Example returned lengths:
+ * normalize_length(4) = 4, normalize_length(5) = 8, normalize_length(6)
+ * = 8, normalize_length(7) = 8, normalize_length(8) = 8
+ * normalize_length(9) = 12, normalize_length(10) = 12, normalize_length(11) =
+ * 12, normalize_length(12) = 12, normalize_length(13) = 13...
+ */
+uint16_t normalize_pcep_tlv_length(uint16_t length)
+{
+ return (length % 4 == 0) ? length : (length + (4 - (length % 4)));
+}
+
+/*
+ * Encoding functions
+ */
+uint16_t pcep_encode_object(struct pcep_object_header *object_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf)
+{
+
+ if (object_hdr->object_class >= MAX_OBJECT_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot encode unknown Object class [%d]",
+ __func__, object_hdr->object_class);
+ return 0;
+ }
+
+ object_encoder_funcptr obj_encoder =
+ object_encoders[object_hdr->object_class];
+ if (obj_encoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object encoder found for Object class [%d]",
+ __func__, object_hdr->object_class);
+ return 0;
+ }
+
+ uint16_t object_length = OBJECT_HEADER_LENGTH
+ + obj_encoder(object_hdr, versioning,
+ buf + OBJECT_HEADER_LENGTH);
+ double_linked_list_node *node =
+ (object_hdr->tlv_list == NULL ? NULL
+ : object_hdr->tlv_list->head);
+ for (; node != NULL; node = node->next_node) {
+ /* Returns the length of the TLV, including the TLV header */
+ object_length += pcep_encode_tlv(
+ (struct pcep_object_tlv_header *)node->data, versioning,
+ buf + object_length);
+ }
+ object_length = normalize_pcep_tlv_length(object_length);
+ write_object_header(object_hdr, object_length, buf);
+ object_hdr->encoded_object = buf;
+ object_hdr->encoded_object_length = object_length;
+
+ return object_length;
+}
+
+
+/* Object Header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Object-Class | OT |Res|P|I| Object Length (bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * // (Object body) //
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+void write_object_header(struct pcep_object_header *object_hdr,
+ uint16_t object_length, uint8_t *buf)
+{
+ buf[0] = object_hdr->object_class;
+ buf[1] = ((object_hdr->object_type << 4)
+ | (object_hdr->flag_p ? OBJECT_HEADER_FLAG_P : 0x00)
+ | (object_hdr->flag_i ? OBJECT_HEADER_FLAG_I : 0x00));
+ uint16_t net_order_length = htons(object_length);
+ memcpy(buf + 2, &net_order_length, sizeof(net_order_length));
+}
+
+
+/*
+ * Functions to encode objects
+ * - they will be passed a pointer to a buffer to write the object body,
+ * which is past the object header.
+ * - they should return the object body length, not including the object header
+ * length.
+ */
+
+uint16_t pcep_encode_obj_open(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_open *open = (struct pcep_object_open *)hdr;
+ obj_body_buf[0] = (open->open_version << 5) & 0xe0;
+ obj_body_buf[1] = open->open_keepalive;
+ obj_body_buf[2] = open->open_deadtimer;
+ obj_body_buf[3] = open->open_sid;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_rp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_rp *rp = (struct pcep_object_rp *)hdr;
+ obj_body_buf[3] = ((rp->flag_strict ? OBJECT_RP_FLAG_O : 0x00)
+ | (rp->flag_bidirectional ? OBJECT_RP_FLAG_B : 0x00)
+ | (rp->flag_reoptimization ? OBJECT_RP_FLAG_R : 0x00)
+ | (rp->flag_of ? OBJECT_RP_FLAG_OF : 0x00)
+ | (rp->priority & 0x07));
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ *uint32_ptr = htonl(rp->request_id);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_notify(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_notify *notify = (struct pcep_object_notify *)hdr;
+ obj_body_buf[2] = notify->notification_type;
+ obj_body_buf[3] = notify->notification_value;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_nopath(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_nopath *nopath = (struct pcep_object_nopath *)hdr;
+ obj_body_buf[0] = nopath->ni;
+ obj_body_buf[1] = ((nopath->flag_c) ? OBJECT_NOPATH_FLAG_C : 0x00);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_association(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ uint16_t *uint16_ptr = (uint16_t *)obj_body_buf;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) {
+ struct pcep_object_association_ipv4 *ipv4 =
+ (struct pcep_object_association_ipv4 *)hdr;
+ obj_body_buf[3] =
+ (ipv4->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00);
+ uint16_ptr[2] = htons(ipv4->association_type);
+ uint16_ptr[3] = htons(ipv4->association_id);
+ uint32_ptr[2] = ipv4->src.s_addr;
+
+ return LENGTH_3WORDS;
+ } else {
+ struct pcep_object_association_ipv6 *ipv6 =
+ (struct pcep_object_association_ipv6 *)hdr;
+ obj_body_buf[3] =
+ (ipv6->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00);
+ uint16_ptr[2] = htons(ipv6->association_type);
+ uint16_ptr[3] = htons(ipv6->association_id);
+ memcpy(uint32_ptr, &ipv6->src, sizeof(struct in6_addr));
+
+ return LENGTH_6WORDS;
+ }
+}
+
+uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ struct pcep_object_endpoints_ipv4 *ipv4 =
+ (struct pcep_object_endpoints_ipv4 *)hdr;
+ uint32_ptr[0] = ipv4->src_ipv4.s_addr;
+ uint32_ptr[1] = ipv4->dst_ipv4.s_addr;
+
+ return LENGTH_2WORDS;
+ } else {
+ struct pcep_object_endpoints_ipv6 *ipv6 =
+ (struct pcep_object_endpoints_ipv6 *)hdr;
+ memcpy(uint32_ptr, &ipv6->src_ipv6, sizeof(struct in6_addr));
+ memcpy(&uint32_ptr[4], &ipv6->dst_ipv6,
+ sizeof(struct in6_addr));
+
+ return LENGTH_8WORDS;
+ }
+}
+
+uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_bandwidth *bandwidth =
+ (struct pcep_object_bandwidth *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ /* Seems like the compiler doesnt correctly copy the float, so memcpy()
+ * it */
+ memcpy(uint32_ptr, &(bandwidth->bandwidth), sizeof(uint32_t));
+ *uint32_ptr = htonl(*uint32_ptr);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_metric(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_metric *metric = (struct pcep_object_metric *)hdr;
+ obj_body_buf[2] = ((metric->flag_c ? OBJECT_METRIC_FLAC_C : 0x00)
+ | (metric->flag_b ? OBJECT_METRIC_FLAC_B : 0x00));
+ obj_body_buf[3] = metric->type;
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ /* Seems like the compiler doesnt correctly copy the float, so memcpy()
+ * it */
+ memcpy(uint32_ptr, &(metric->value), sizeof(uint32_t));
+ *uint32_ptr = htonl(*uint32_ptr);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_lspa(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_lspa *lspa = (struct pcep_object_lspa *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl(lspa->lspa_exclude_any);
+ uint32_ptr[1] = htonl(lspa->lspa_include_any);
+ uint32_ptr[2] = htonl(lspa->lspa_include_all);
+ obj_body_buf[12] = lspa->setup_priority;
+ obj_body_buf[13] = lspa->holding_priority;
+ obj_body_buf[14] =
+ (lspa->flag_local_protection ? OBJECT_LSPA_FLAG_L : 0x00);
+
+ return LENGTH_4WORDS;
+}
+
+uint16_t pcep_encode_obj_svec(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_svec *svec = (struct pcep_object_svec *)hdr;
+ obj_body_buf[3] =
+ ((svec->flag_srlg_diverse ? OBJECT_SVEC_FLAG_S : 0x00)
+ | (svec->flag_node_diverse ? OBJECT_SVEC_FLAG_N : 0x00)
+ | (svec->flag_link_diverse ? OBJECT_SVEC_FLAG_L : 0x00));
+
+ if (svec->request_id_list == NULL) {
+ return LENGTH_1WORD;
+ }
+
+ int index = 1;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ double_linked_list_node *node = svec->request_id_list->head;
+ for (; node != NULL; node = node->next_node) {
+ uint32_ptr[index++] = htonl(*((uint32_t *)(node->data)));
+ }
+
+ return LENGTH_1WORD
+ + (svec->request_id_list->num_entries * sizeof(uint32_t));
+}
+
+uint16_t pcep_encode_obj_error(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_error *error = (struct pcep_object_error *)hdr;
+ obj_body_buf[2] = error->error_type;
+ obj_body_buf[3] = error->error_value;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_close(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_close *close = (struct pcep_object_close *)hdr;
+ obj_body_buf[3] = close->reason;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_srp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)hdr;
+ obj_body_buf[3] = (srp->flag_lsp_remove ? OBJECT_SRP_FLAG_R : 0x00);
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4);
+ *uint32_ptr = htonl(srp->srp_id_number);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_lsp(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl((lsp->plsp_id << 12) & 0xfffff000);
+ obj_body_buf[3] = ((lsp->flag_c ? OBJECT_LSP_FLAG_C : 0x00)
+ | ((lsp->operational_status << 4) & 0x70)
+ | (lsp->flag_a ? OBJECT_LSP_FLAG_A : 0x00)
+ | (lsp->flag_r ? OBJECT_LSP_FLAG_R : 0x00)
+ | (lsp->flag_s ? OBJECT_LSP_FLAG_S : 0x00)
+ | (lsp->flag_d ? OBJECT_LSP_FLAG_D : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)hdr;
+ uint32_t *uint32_ptr = (uint32_t *)obj_body_buf;
+ uint32_ptr[0] = htonl(obj->enterprise_number);
+ uint32_ptr[1] = htonl(obj->enterprise_specific_info);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)hdr;
+ obj_body_buf[3] = ((obj->flag_i ? OBJECT_INTER_LAYER_FLAG_I : 0x00)
+ | (obj->flag_m ? OBJECT_INTER_LAYER_FLAG_M : 0x00)
+ | (obj->flag_t ? OBJECT_INTER_LAYER_FLAG_T : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)hdr;
+ uint8_t buf_index = 0;
+
+ double_linked_list_node *node = obj->switch_layer_rows->head;
+ while (node != NULL) {
+ struct pcep_object_switch_layer_row *row = node->data;
+ if (row == NULL) {
+ break;
+ }
+
+ obj_body_buf[buf_index] = row->lsp_encoding_type;
+ obj_body_buf[buf_index + 1] = row->switching_type;
+ obj_body_buf[buf_index + 3] =
+ (row->flag_i ? OBJECT_SWITCH_LAYER_FLAG_I : 0x00);
+
+ buf_index += LENGTH_1WORD;
+ }
+
+ return buf_index;
+}
+
+uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)hdr;
+
+ obj_body_buf[0] = obj->switching_capability;
+ obj_body_buf[1] = obj->encoding;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)hdr;
+
+ obj_body_buf[0] = obj->switching_capability;
+ obj_body_buf[1] = obj->encoding;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)hdr;
+
+ uint16_t *uint16_ptr = (uint16_t *)obj_body_buf;
+ *uint16_ptr = htons(obj->of_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_obj_ro(struct pcep_object_header *hdr,
+ struct pcep_versioning *versioning,
+ uint8_t *obj_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_ro *ro = (struct pcep_object_ro *)hdr;
+ if (ro == NULL || ro->sub_objects == NULL) {
+ return 0;
+ }
+
+ /* RO Subobject format
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ * |L| Type | Length | (Subobject contents) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ */
+
+ uint16_t index = 0;
+ double_linked_list_node *node = ro->sub_objects->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj = node->data;
+ obj_body_buf[index++] =
+ ((ro_subobj->flag_subobj_loose_hop ? 0x80 : 0x00)
+ | (ro_subobj->ro_subobj_type));
+ /* The length will be written below, depending on the subobj
+ * type */
+ uint8_t *length_ptr = &(obj_body_buf[index++]);
+ uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + index);
+
+ /* - The index has already been incremented past the header,
+ * and now points to the ro_subobj body. Below it just needs
+ * to be incremented past the body.
+ *
+ * - Each section below needs to write the total length,
+ * including the 2 byte subobj header. */
+
+ switch (ro_subobj->ro_subobj_type) {
+ case RO_SUBOBJ_TYPE_IPV4: {
+ struct pcep_ro_subobj_ipv4 *ipv4 =
+ (struct pcep_ro_subobj_ipv4 *)ro_subobj;
+ uint32_ptr[0] = ipv4->ip_addr.s_addr;
+ index += LENGTH_1WORD;
+ obj_body_buf[index++] = ipv4->prefix_length;
+ obj_body_buf[index++] =
+ (ipv4->flag_local_protection
+ ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT
+ : 0x00);
+ *length_ptr = LENGTH_2WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_IPV6: {
+ struct pcep_ro_subobj_ipv6 *ipv6 =
+ (struct pcep_ro_subobj_ipv6 *)ro_subobj;
+ encode_ipv6(&ipv6->ip_addr, uint32_ptr);
+ index += LENGTH_4WORDS;
+ obj_body_buf[index++] = ipv6->prefix_length;
+ obj_body_buf[index++] =
+ (ipv6->flag_local_protection
+ ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT
+ : 0x00);
+ *length_ptr = LENGTH_5WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_LABEL: {
+ struct pcep_ro_subobj_32label *label =
+ (struct pcep_ro_subobj_32label *)ro_subobj;
+ obj_body_buf[index++] =
+ (label->flag_global_label
+ ? OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL
+ : 0x00);
+ obj_body_buf[index++] = label->class_type;
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ *uint32_ptr = htonl(label->label);
+ *length_ptr = LENGTH_2WORDS;
+ index += LENGTH_1WORD;
+ } break;
+
+ case RO_SUBOBJ_TYPE_UNNUM: {
+ struct pcep_ro_subobj_unnum *unum =
+ (struct pcep_ro_subobj_unnum *)ro_subobj;
+ index += 2; /* increment past 2 reserved bytes */
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ uint32_ptr[0] = unum->router_id.s_addr;
+ uint32_ptr[1] = htonl(unum->interface_id);
+ *length_ptr = LENGTH_3WORDS;
+ index += LENGTH_2WORDS;
+ } break;
+
+ case RO_SUBOBJ_TYPE_ASN: {
+ struct pcep_ro_subobj_asn *asn =
+ (struct pcep_ro_subobj_asn *)ro_subobj;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(obj_body_buf + index);
+ *uint16_ptr = htons(asn->asn);
+ *length_ptr = LENGTH_1WORD;
+ index += 2;
+ } break;
+
+ case RO_SUBOBJ_TYPE_SR: {
+ /* SR-ERO subobject format
+ *
+ * 0 1 2 3 0 1 2 3 4
+ * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |L| Type=36 | Length | NT | Flags
+ * |F|S|C|M|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SID (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * // NAI (variable, optional) //
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ struct pcep_ro_subobj_sr *sr_subobj =
+ (struct pcep_ro_subobj_sr *)ro_subobj;
+ obj_body_buf[index++] =
+ ((sr_subobj->nai_type << 4) & 0xf0);
+ obj_body_buf[index++] =
+ ((sr_subobj->flag_f ? OBJECT_SUBOBJ_SR_FLAG_F
+ : 0x00)
+ | (sr_subobj->flag_s ? OBJECT_SUBOBJ_SR_FLAG_S
+ : 0x00)
+ | (sr_subobj->flag_c ? OBJECT_SUBOBJ_SR_FLAG_C
+ : 0x00)
+ | (sr_subobj->flag_m ? OBJECT_SUBOBJ_SR_FLAG_M
+ : 0x00));
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ /* Start with LENGTH_1WORD for the SubObj HDR + NT +
+ * Flags */
+ uint8_t sr_base_length = LENGTH_1WORD;
+ /* If the sid_absent flag is true, then dont convert the
+ * sid */
+ if (sr_subobj->flag_s == false) {
+ uint32_ptr[0] = htonl(sr_subobj->sid);
+ index += LENGTH_1WORD;
+ uint32_ptr = (uint32_t *)(obj_body_buf + index);
+ sr_base_length += LENGTH_1WORD;
+ }
+
+ /* The lengths below need to include:
+ * - sr_base_length: set above to include SR SubObj Hdr
+ * and the SID if present
+ * - Number of bytes written to the NAI
+ * The index will only be incremented below by the
+ * number of bytes written to the NAI, since the RO SR
+ * subobj header and the SID have already been written.
+ */
+
+ double_linked_list_node *nai_node =
+ (sr_subobj->nai_list == NULL
+ ? NULL
+ : sr_subobj->nai_list->head);
+ if (nai_node == NULL) {
+ if (sr_subobj->nai_type
+ == PCEP_SR_SUBOBJ_NAI_ABSENT) {
+ *length_ptr = sr_base_length;
+ continue;
+ } else {
+ return 0;
+ }
+ }
+ switch (sr_subobj->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_1WORD;
+ index += LENGTH_1WORD;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ *length_ptr = sr_base_length + LENGTH_4WORDS;
+ index += LENGTH_4WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[1] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[2] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[3] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_4WORDS;
+ index += LENGTH_4WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ uint32_ptr[0] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ uint32_ptr[1] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_2WORDS;
+ index += LENGTH_2WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ nai_node = nai_node->next_node;
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr + 4);
+ *length_ptr = sr_base_length + LENGTH_8WORDS;
+ index += LENGTH_8WORDS;
+ break;
+
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr);
+ nai_node = nai_node->next_node;
+ uint32_ptr[4] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ nai_node = nai_node->next_node;
+ encode_ipv6((struct in6_addr *)nai_node->data,
+ uint32_ptr + 5);
+ nai_node = nai_node->next_node;
+ uint32_ptr[9] =
+ ((struct in_addr *)nai_node->data)
+ ->s_addr;
+ *length_ptr = sr_base_length + LENGTH_10WORDS;
+ index += LENGTH_10WORDS;
+ break;
+
+ default:
+ break;
+ }
+ } break;
+
+ default:
+ break;
+ }
+ }
+
+ return index;
+}
+
+void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst)
+{
+ memcpy(dst, src_ipv6, sizeof(struct in6_addr));
+}
+
+/*
+ * Decoding functions.
+ */
+
+void pcep_decode_object_hdr(const uint8_t *obj_buf,
+ struct pcep_object_header *obj_hdr)
+{
+ memset(obj_hdr, 0, sizeof(struct pcep_object_header));
+
+ obj_hdr->object_class = obj_buf[0];
+ obj_hdr->object_type = (obj_buf[1] >> 4) & 0x0f;
+ obj_hdr->flag_p = (obj_buf[1] & OBJECT_HEADER_FLAG_P);
+ obj_hdr->flag_i = (obj_buf[1] & OBJECT_HEADER_FLAG_I);
+ uint16_t net_order_length;
+ memcpy(&net_order_length, obj_buf + 2, sizeof(net_order_length));
+ obj_hdr->encoded_object_length = ntohs(net_order_length);
+ obj_hdr->encoded_object = obj_buf;
+}
+
+uint16_t pcep_object_get_length(enum pcep_object_classes object_class,
+ enum pcep_object_types object_type)
+{
+ uint8_t object_length = pcep_object_class_lengths[object_class];
+ if (object_length == 0) {
+ if (object_class == PCEP_OBJ_CLASS_ENDPOINTS) {
+ if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ return 12;
+ } else if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ return 36;
+ }
+ }
+
+ return 0;
+ }
+
+ return object_length;
+}
+
+uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr)
+{
+ return (pcep_object_get_length(object_hdr->object_class,
+ object_hdr->object_type));
+}
+
+bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr)
+{
+ uint8_t object_length = pcep_object_get_length_by_hdr(object_hdr);
+ if (object_length == 0) {
+ return false;
+ }
+
+ return (object_hdr->encoded_object_length - object_length) > 0;
+}
+
+struct pcep_object_header *pcep_decode_object(const uint8_t *obj_buf)
+{
+
+ struct pcep_object_header object_hdr;
+ /* Only initializes and decodes the Object Header: class, type, flags,
+ * and length */
+ pcep_decode_object_hdr(obj_buf, &object_hdr);
+
+ if (object_hdr.object_class >= MAX_OBJECT_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot decode unknown Object class [%d]",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ object_decoder_funcptr obj_decoder =
+ object_decoders[object_hdr.object_class];
+ if (obj_decoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object decoder found for Object class [%d]",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ /* The object decoders will start decoding the object body, if
+ * anything from the header is needed, they have the object_hdr */
+ struct pcep_object_header *object =
+ obj_decoder(&object_hdr, obj_buf + OBJECT_HEADER_LENGTH);
+ if (object == NULL) {
+ pcep_log(LOG_INFO, "%s: Unable to decode Object class [%d].",
+ __func__, object_hdr.object_class);
+ return NULL;
+ }
+
+ if (pcep_object_has_tlvs(&object_hdr)) {
+ object->tlv_list = dll_initialize();
+ int num_iterations = 0;
+ uint16_t tlv_index = pcep_object_get_length_by_hdr(&object_hdr);
+ while ((object->encoded_object_length - tlv_index) > 0
+ && num_iterations++ < MAX_ITERATIONS) {
+ struct pcep_object_tlv_header *tlv =
+ pcep_decode_tlv(obj_buf + tlv_index);
+ if (tlv == NULL) {
+ /* TODO should we do anything else here ? */
+ return object;
+ }
+
+ /* The TLV length does not include the TLV header */
+ tlv_index += normalize_pcep_tlv_length(
+ tlv->encoded_tlv_length + TLV_HEADER_LENGTH);
+ dll_append(object->tlv_list, tlv);
+ }
+ }
+
+ return object;
+}
+
+static struct pcep_object_header *
+common_object_create(struct pcep_object_header *hdr, uint16_t new_obj_length)
+{
+ struct pcep_object_header *new_object =
+ pceplib_malloc(PCEPLIB_MESSAGES, new_obj_length);
+ memset(new_object, 0, new_obj_length);
+ memcpy(new_object, hdr, sizeof(struct pcep_object_header));
+
+ return new_object;
+}
+
+/*
+ * Decoders
+ */
+
+struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_open *obj =
+ (struct pcep_object_open *)common_object_create(
+ hdr, sizeof(struct pcep_object_open));
+
+ obj->open_version = (obj_buf[0] >> 5) & 0x07;
+ obj->open_keepalive = obj_buf[1];
+ obj->open_deadtimer = obj_buf[2];
+ obj->open_sid = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_rp *obj =
+ (struct pcep_object_rp *)common_object_create(
+ hdr, sizeof(struct pcep_object_rp));
+
+ obj->flag_reoptimization = (obj_buf[3] & OBJECT_RP_FLAG_R);
+ obj->flag_bidirectional = (obj_buf[3] & OBJECT_RP_FLAG_B);
+ obj->flag_strict = (obj_buf[3] & OBJECT_RP_FLAG_O);
+ obj->flag_of = (obj_buf[3] & OBJECT_RP_FLAG_OF);
+ obj->priority = (obj_buf[3] & 0x07);
+ obj->request_id = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_notify *obj =
+ (struct pcep_object_notify *)common_object_create(
+ hdr, sizeof(struct pcep_object_notify));
+
+ obj->notification_type = obj_buf[2];
+ obj->notification_value = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_nopath *obj =
+ (struct pcep_object_nopath *)common_object_create(
+ hdr, sizeof(struct pcep_object_nopath));
+
+ obj->ni = (obj_buf[0] >> 1);
+ obj->flag_c = (obj_buf[0] & OBJECT_NOPATH_FLAG_C);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_association(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ uint16_t *uint16_ptr = (uint16_t *)obj_buf;
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) {
+ struct pcep_object_association_ipv4 *obj =
+ (struct pcep_object_association_ipv4 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_association_ipv4));
+ obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R);
+ obj->association_type = ntohs(uint16_ptr[2]);
+ obj->association_id = ntohs(uint16_ptr[3]);
+ obj->src.s_addr = uint32_ptr[2];
+
+ return (struct pcep_object_header *)obj;
+ } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ struct pcep_object_association_ipv6 *obj =
+ (struct pcep_object_association_ipv6 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_association_ipv6));
+
+ obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R);
+ obj->association_type = ntohs(uint16_ptr[2]);
+ obj->association_id = ntohs(uint16_ptr[3]);
+ memcpy(&obj->src, &uint32_ptr[2], sizeof(struct in6_addr));
+
+ return (struct pcep_object_header *)obj;
+ }
+
+ return NULL;
+}
+struct pcep_object_header *
+pcep_decode_obj_endpoints(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) {
+ struct pcep_object_endpoints_ipv4 *obj =
+ (struct pcep_object_endpoints_ipv4 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_endpoints_ipv4));
+ obj->src_ipv4.s_addr = uint32_ptr[0];
+ obj->dst_ipv4.s_addr = uint32_ptr[1];
+
+ return (struct pcep_object_header *)obj;
+ } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) {
+ struct pcep_object_endpoints_ipv6 *obj =
+ (struct pcep_object_endpoints_ipv6 *)
+ common_object_create(
+ hdr,
+ sizeof(struct
+ pcep_object_endpoints_ipv6));
+
+ memcpy(&obj->src_ipv6, &uint32_ptr[0], sizeof(struct in6_addr));
+ memcpy(&obj->dst_ipv6, &uint32_ptr[4], sizeof(struct in6_addr));
+
+ return (struct pcep_object_header *)obj;
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_bandwidth(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_bandwidth *obj =
+ (struct pcep_object_bandwidth *)common_object_create(
+ hdr, sizeof(struct pcep_object_bandwidth));
+
+ uint32_t value = ntohl(*((uint32_t *)obj_buf));
+ /* Seems like the compiler doesnt correctly copy to the float, so
+ * memcpy() it */
+ memcpy(&obj->bandwidth, &value, sizeof(uint32_t));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *obj_buf)
+{
+ struct pcep_object_metric *obj =
+ (struct pcep_object_metric *)common_object_create(
+ hdr, sizeof(struct pcep_object_metric));
+ obj->flag_b = (obj_buf[2] & OBJECT_METRIC_FLAC_B);
+ obj->flag_c = (obj_buf[2] & OBJECT_METRIC_FLAC_C);
+ obj->type = obj_buf[3];
+ uint32_t value = ntohl(*((uint32_t *)(obj_buf + 4)));
+ /* Seems like the compiler doesnt correctly copy to the float, so
+ * memcpy() it */
+ memcpy(&obj->value, &value, sizeof(uint32_t));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_lspa *obj =
+ (struct pcep_object_lspa *)common_object_create(
+ hdr, sizeof(struct pcep_object_lspa));
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+
+ obj->lspa_exclude_any = ntohl(uint32_ptr[0]);
+ obj->lspa_include_any = ntohl(uint32_ptr[1]);
+ obj->lspa_include_all = ntohl(uint32_ptr[2]);
+ obj->setup_priority = obj_buf[12];
+ obj->holding_priority = obj_buf[13];
+ obj->flag_local_protection = (obj_buf[14] & OBJECT_LSPA_FLAG_L);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_svec *obj =
+ (struct pcep_object_svec *)common_object_create(
+ hdr, sizeof(struct pcep_object_svec));
+
+ obj->flag_link_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_L);
+ obj->flag_node_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_N);
+ obj->flag_srlg_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_S);
+
+ if (hdr->encoded_object_length > LENGTH_2WORDS) {
+ obj->request_id_list = dll_initialize();
+ uint16_t index = 1;
+ uint32_t *uint32_ptr = (uint32_t *)obj_buf;
+ for (;
+ index < ((hdr->encoded_object_length - LENGTH_2WORDS) / 4);
+ index++) {
+ uint32_t *req_id_ptr = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(uint32_t));
+ *req_id_ptr = uint32_ptr[index];
+ dll_append(obj->request_id_list, req_id_ptr);
+ }
+ }
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_error *obj =
+ (struct pcep_object_error *)common_object_create(
+ hdr, sizeof(struct pcep_object_error));
+
+ obj->error_type = obj_buf[2];
+ obj->error_value = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_close *obj =
+ (struct pcep_object_close *)common_object_create(
+ hdr, sizeof(struct pcep_object_close));
+
+ obj->reason = obj_buf[3];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_srp *obj =
+ (struct pcep_object_srp *)common_object_create(
+ hdr, sizeof(struct pcep_object_srp));
+
+ obj->flag_lsp_remove = (obj_buf[3] & OBJECT_SRP_FLAG_R);
+ obj->srp_id_number = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_lsp *obj =
+ (struct pcep_object_lsp *)common_object_create(
+ hdr, sizeof(struct pcep_object_lsp));
+
+ obj->flag_d = (obj_buf[3] & OBJECT_LSP_FLAG_D);
+ obj->flag_s = (obj_buf[3] & OBJECT_LSP_FLAG_S);
+ obj->flag_r = (obj_buf[3] & OBJECT_LSP_FLAG_R);
+ obj->flag_a = (obj_buf[3] & OBJECT_LSP_FLAG_A);
+ obj->flag_c = (obj_buf[3] & OBJECT_LSP_FLAG_C);
+ obj->operational_status = ((obj_buf[3] >> 4) & 0x07);
+ obj->plsp_id = ((ntohl(*((uint32_t *)obj_buf)) >> 12) & 0x000fffff);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_vendor_info(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_vendor_info *obj =
+ (struct pcep_object_vendor_info *)common_object_create(
+ hdr, sizeof(struct pcep_object_vendor_info));
+ obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf)));
+ obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4)));
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_inter_layer(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_inter_layer *obj =
+ (struct pcep_object_inter_layer *)common_object_create(
+ hdr, sizeof(struct pcep_object_inter_layer));
+ obj->flag_t = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_T);
+ obj->flag_m = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_M);
+ obj->flag_i = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_I);
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_switch_layer(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_switch_layer *obj =
+ (struct pcep_object_switch_layer *)common_object_create(
+ hdr, sizeof(struct pcep_object_switch_layer));
+ obj->switch_layer_rows = dll_initialize();
+ int num_rows = ((hdr->encoded_object_length - 4) / 4);
+ uint8_t buf_index = 0;
+
+ int i = 0;
+ for (; i < num_rows; i++) {
+ struct pcep_object_switch_layer_row *row = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_object_switch_layer_row));
+ row->lsp_encoding_type = obj_buf[buf_index];
+ row->switching_type = obj_buf[buf_index + 1];
+ row->flag_i =
+ (obj_buf[buf_index + 3] & OBJECT_SWITCH_LAYER_FLAG_I);
+ dll_append(obj->switch_layer_rows, row);
+
+ buf_index += LENGTH_1WORD;
+ }
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_req_adap_cap *obj =
+ (struct pcep_object_req_adap_cap *)common_object_create(
+ hdr, sizeof(struct pcep_object_req_adap_cap));
+
+ obj->switching_capability = obj_buf[0];
+ obj->encoding = obj_buf[1];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_server_ind(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_server_indication *obj =
+ (struct pcep_object_server_indication *)common_object_create(
+ hdr, sizeof(struct pcep_object_server_indication));
+
+ obj->switching_capability = obj_buf[0];
+ obj->encoding = obj_buf[1];
+
+ return (struct pcep_object_header *)obj;
+}
+
+struct pcep_object_header *
+pcep_decode_obj_objective_function(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_objective_function *obj =
+ (struct pcep_object_objective_function *)common_object_create(
+ hdr, sizeof(struct pcep_object_objective_function));
+
+ uint16_t *uint16_ptr = (uint16_t *)obj_buf;
+ obj->of_code = ntohs(*uint16_ptr);
+
+ return (struct pcep_object_header *)obj;
+}
+
+void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l,
+ uint8_t subobj_type)
+{
+ subobj->flag_subobj_loose_hop = flag_l;
+ subobj->ro_subobj_type = subobj_type;
+}
+
+void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6)
+{
+ memcpy(dst_ipv6, src, sizeof(struct in6_addr));
+}
+struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr,
+ const uint8_t *obj_buf)
+{
+ struct pcep_object_ro *obj =
+ (struct pcep_object_ro *)common_object_create(
+ hdr, sizeof(struct pcep_object_ro));
+ obj->sub_objects = dll_initialize();
+
+ /* RO Subobject format
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ * |L| Type | Length | (Subobject contents) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+
+ */
+
+ uint16_t read_count = 0;
+ int num_sub_objects = 1;
+ uint32_t *uint32_ptr;
+ uint16_t obj_body_length =
+ hdr->encoded_object_length - OBJECT_HEADER_LENGTH;
+
+ while ((obj_body_length - read_count) > OBJECT_RO_SUBOBJ_HEADER_LENGTH
+ && num_sub_objects < MAX_ITERATIONS) {
+ num_sub_objects++;
+ /* Read the Sub-Object Header */
+ bool flag_l = (obj_buf[read_count] & 0x80);
+ uint8_t subobj_type = (obj_buf[read_count++] & 0x7f);
+ uint8_t subobj_length = obj_buf[read_count++];
+
+ if (subobj_length <= OBJECT_RO_SUBOBJ_HEADER_LENGTH) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid ro subobj type [%d] length [%d]",
+ __func__, subobj_type, subobj_length);
+ pceplib_free(PCEPLIB_MESSAGES, obj);
+ return NULL;
+ }
+
+ switch (subobj_type) {
+ case RO_SUBOBJ_TYPE_IPV4: {
+ struct pcep_ro_subobj_ipv4 *ipv4 = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_ipv4));
+ ipv4->ro_subobj.flag_subobj_loose_hop = flag_l;
+ ipv4->ro_subobj.ro_subobj_type = subobj_type;
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ ipv4->ip_addr.s_addr = *uint32_ptr;
+ read_count += LENGTH_1WORD;
+ ipv4->prefix_length = obj_buf[read_count++];
+ ipv4->flag_local_protection =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+
+ dll_append(obj->sub_objects, ipv4);
+ } break;
+
+ case RO_SUBOBJ_TYPE_IPV6: {
+ struct pcep_ro_subobj_ipv6 *ipv6 = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_ipv6));
+ ipv6->ro_subobj.flag_subobj_loose_hop = flag_l;
+ ipv6->ro_subobj.ro_subobj_type = subobj_type;
+ decode_ipv6((uint32_t *)obj_buf, &ipv6->ip_addr);
+ read_count += LENGTH_4WORDS;
+ ipv6->prefix_length = obj_buf[read_count++];
+ ipv6->flag_local_protection =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+
+ dll_append(obj->sub_objects, ipv6);
+ } break;
+
+ case RO_SUBOBJ_TYPE_LABEL: {
+ struct pcep_ro_subobj_32label *label = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_32label));
+ label->ro_subobj.flag_subobj_loose_hop = flag_l;
+ label->ro_subobj.ro_subobj_type = subobj_type;
+ label->flag_global_label =
+ (obj_buf[read_count++]
+ & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL);
+ label->class_type = obj_buf[read_count++];
+ label->label = ntohl(obj_buf[read_count]);
+ read_count += LENGTH_1WORD;
+
+ dll_append(obj->sub_objects, label);
+ } break;
+
+ case RO_SUBOBJ_TYPE_UNNUM: {
+ struct pcep_ro_subobj_unnum *unum = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_unnum));
+ unum->ro_subobj.flag_subobj_loose_hop = flag_l;
+ unum->ro_subobj.ro_subobj_type = subobj_type;
+ set_ro_subobj_fields(
+ (struct pcep_object_ro_subobj *)unum, flag_l,
+ subobj_type);
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ unum->interface_id = ntohl(uint32_ptr[0]);
+ unum->router_id.s_addr = uint32_ptr[1];
+ read_count += 2;
+
+ dll_append(obj->sub_objects, unum);
+ } break;
+
+ case RO_SUBOBJ_TYPE_ASN: {
+ struct pcep_ro_subobj_asn *asn = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_asn));
+ asn->ro_subobj.flag_subobj_loose_hop = flag_l;
+ asn->ro_subobj.ro_subobj_type = subobj_type;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(obj_buf + read_count);
+ asn->asn = ntohs(*uint16_ptr);
+ read_count += 2;
+
+ dll_append(obj->sub_objects, asn);
+ } break;
+
+ case RO_SUBOBJ_TYPE_SR: {
+ /* SR-ERO subobject format
+ *
+ * 0 1 2 3 0 1 2 3 4
+ * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |L| Type=36 | Length | NT | Flags
+ * |F|S|C|M|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SID (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * // NAI (variable, optional) //
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ struct pcep_ro_subobj_sr *sr_subobj = pceplib_malloc(
+ PCEPLIB_MESSAGES,
+ sizeof(struct pcep_ro_subobj_sr));
+ sr_subobj->ro_subobj.flag_subobj_loose_hop = flag_l;
+ sr_subobj->ro_subobj.ro_subobj_type = subobj_type;
+ dll_append(obj->sub_objects, sr_subobj);
+
+ sr_subobj->nai_list = dll_initialize();
+ sr_subobj->nai_type =
+ ((obj_buf[read_count++] >> 4) & 0x0f);
+ sr_subobj->flag_f =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_F);
+ sr_subobj->flag_s =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_S);
+ sr_subobj->flag_c =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_C);
+ sr_subobj->flag_m =
+ (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_M);
+ read_count++;
+
+ /* If the sid_absent flag is true, then dont decode the
+ * sid */
+ uint32_ptr = (uint32_t *)(obj_buf + read_count);
+ if (sr_subobj->flag_s == false) {
+ sr_subobj->sid = ntohl(*uint32_ptr);
+ read_count += LENGTH_1WORD;
+ uint32_ptr += 1;
+ }
+
+ switch (sr_subobj->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = *uint32_ptr;
+ dll_append(sr_subobj->nai_list, ipv4);
+ read_count += LENGTH_1WORD;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+ read_count += LENGTH_4WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[0];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[1];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[2];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[3];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_4WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: {
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[0];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[1];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_2WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ ipv6 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr + LENGTH_4WORDS, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ read_count += LENGTH_8WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY: {
+ struct in6_addr *ipv6 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ struct in_addr *ipv4 =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[LENGTH_4WORDS];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ ipv6 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in6_addr));
+ decode_ipv6(uint32_ptr + LENGTH_5WORDS, ipv6);
+ dll_append(sr_subobj->nai_list, ipv6);
+
+ ipv4 = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct in_addr));
+ ipv4->s_addr = uint32_ptr[LENGTH_9WORDS];
+ dll_append(sr_subobj->nai_list, ipv4);
+
+ read_count += LENGTH_10WORDS;
+ } break;
+
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ default:
+ break;
+ }
+ } break;
+
+ default:
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_decode_obj_ro skipping unrecognized sub-object type [%d]",
+ __func__, subobj_type);
+ read_count += subobj_length;
+ break;
+ }
+ }
+
+ return (struct pcep_object_header *)obj;
+}
diff --git a/pceplib/pcep_msg_tlvs.c b/pceplib/pcep_msg_tlvs.c
new file mode 100644
index 0000000000..890da9517f
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs.c
@@ -0,0 +1,464 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This is the implementation of a High Level PCEP message object TLV API.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_tlvs.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_memory.h"
+
+static struct pcep_object_tlv_header *
+pcep_tlv_common_create(enum pcep_object_tlv_types type, uint16_t size)
+{
+ struct pcep_object_tlv_header *tlv =
+ pceplib_malloc(PCEPLIB_MESSAGES, size);
+ memset(tlv, 0, size);
+ tlv->type = type;
+
+ return tlv;
+}
+
+/*
+ * Open Object TLVs
+ */
+
+struct pcep_object_tlv_stateful_pce_capability *
+pcep_tlv_create_stateful_pce_capability(
+ bool flag_u_lsp_update_capability, bool flag_s_include_db_version,
+ bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync,
+ bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync)
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_stateful_pce_capability));
+ tlv->flag_u_lsp_update_capability = flag_u_lsp_update_capability;
+ tlv->flag_s_include_db_version = flag_s_include_db_version;
+ tlv->flag_i_lsp_instantiation_capability =
+ flag_i_lsp_instantiation_capability;
+ tlv->flag_t_triggered_resync = flag_t_triggered_resync;
+ tlv->flag_d_delta_lsp_sync = flag_d_delta_lsp_sync;
+ tlv->flag_f_triggered_initial_sync = flag_f_triggered_initial_sync;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_lsp_db_version *
+pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version)
+{
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ (struct pcep_object_tlv_lsp_db_version *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION,
+ sizeof(struct pcep_object_tlv_lsp_db_version));
+ tlv->lsp_db_version = lsp_db_version;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_speaker_entity_identifier *
+pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list)
+{
+ if (speaker_entity_id_list == NULL) {
+ return NULL;
+ }
+
+ if (speaker_entity_id_list->num_entries == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ (struct pcep_object_tlv_speaker_entity_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID,
+ sizeof(struct
+ pcep_object_tlv_speaker_entity_identifier));
+ tlv->speaker_entity_id_list = speaker_entity_id_list;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_path_setup_type *
+pcep_tlv_create_path_setup_type(uint8_t pst)
+{
+ struct pcep_object_tlv_path_setup_type *tlv =
+ (struct pcep_object_tlv_path_setup_type *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE,
+ sizeof(struct pcep_object_tlv_path_setup_type));
+ tlv->path_setup_type = pst;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_path_setup_type_capability *
+pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list,
+ double_linked_list *sub_tlv_list)
+{
+ if (pst_list == NULL) {
+ return NULL;
+ }
+
+ if (pst_list->num_entries == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ (struct pcep_object_tlv_path_setup_type_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_path_setup_type_capability));
+
+ tlv->pst_list = pst_list;
+ tlv->sub_tlv_list = sub_tlv_list;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_sr_pce_capability *
+pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x,
+ uint8_t max_sid_depth)
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY,
+ sizeof(struct
+ pcep_object_tlv_sr_pce_capability));
+ tlv->flag_n = flag_n;
+ tlv->flag_x = flag_x;
+ tlv->max_sid_depth = max_sid_depth;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_of_list *
+pcep_tlv_create_of_list(double_linked_list *of_list)
+{
+ if (of_list == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_of_list *tlv =
+ (struct pcep_object_tlv_of_list *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST,
+ sizeof(struct pcep_object_tlv_of_list));
+
+ tlv->of_list = of_list;
+
+ return tlv;
+}
+
+/*
+ * LSP Object TLVs
+ */
+
+struct pcep_object_tlv_ipv4_lsp_identifier *
+pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender,
+ struct in_addr *ipv4_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in_addr *extended_tunnel_id)
+{
+ if (ipv4_tunnel_sender == NULL || ipv4_tunnel_endpoint == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS,
+ sizeof(struct
+ pcep_object_tlv_ipv4_lsp_identifier));
+ tlv->ipv4_tunnel_sender.s_addr = ipv4_tunnel_sender->s_addr;
+ tlv->ipv4_tunnel_endpoint.s_addr = ipv4_tunnel_endpoint->s_addr;
+ tlv->lsp_id = lsp_id;
+ tlv->tunnel_id = tunnel_id;
+ tlv->extended_tunnel_id.s_addr =
+ (extended_tunnel_id == NULL ? INADDR_ANY
+ : extended_tunnel_id->s_addr);
+
+ return tlv;
+}
+
+struct pcep_object_tlv_ipv6_lsp_identifier *
+pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender,
+ struct in6_addr *ipv6_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in6_addr *extended_tunnel_id)
+{
+ if (ipv6_tunnel_sender == NULL || ipv6_tunnel_endpoint == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS,
+ sizeof(struct
+ pcep_object_tlv_ipv6_lsp_identifier));
+
+ memcpy(&tlv->ipv6_tunnel_sender, ipv6_tunnel_sender,
+ sizeof(struct in6_addr));
+
+ tlv->tunnel_id = tunnel_id;
+ tlv->lsp_id = lsp_id;
+
+ memcpy(&tlv->extended_tunnel_id, extended_tunnel_id,
+ sizeof(struct in6_addr));
+
+ memcpy(&tlv->ipv6_tunnel_endpoint, ipv6_tunnel_endpoint,
+ sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_symbolic_path_name *
+pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name,
+ uint16_t symbolic_path_name_length)
+{
+ /* symbolic_path_name_length should NOT include the null terminator and
+ * cannot be zero */
+ if (symbolic_path_name == NULL || symbolic_path_name_length == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME,
+ sizeof(struct
+ pcep_object_tlv_symbolic_path_name));
+
+ uint16_t length = (symbolic_path_name_length > MAX_SYMBOLIC_PATH_NAME)
+ ? MAX_SYMBOLIC_PATH_NAME
+ : symbolic_path_name_length;
+ memcpy(tlv->symbolic_path_name, symbolic_path_name, length);
+ tlv->symbolic_path_name_length = length;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_lsp_error_code *
+pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code)
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ (struct pcep_object_tlv_lsp_error_code *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE,
+ sizeof(struct pcep_object_tlv_lsp_error_code));
+ tlv->lsp_error_code = lsp_error_code;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value)
+{
+ if (error_node_ip == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->c_type = RSVP_ERROR_SPEC_IPV4_CTYPE;
+ tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM;
+ tlv->error_code = error_code;
+ tlv->error_value = error_value;
+ tlv->error_spec_ip.ipv4_error_node_address.s_addr =
+ error_node_ip->s_addr;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value)
+{
+ if (error_node_ip == NULL) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)
+ pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->c_type = RSVP_ERROR_SPEC_IPV6_CTYPE;
+ tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM;
+ tlv->error_code = error_code;
+ tlv->error_value = error_value;
+ memcpy(&tlv->error_spec_ip, error_node_ip, sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_nopath_vector *
+pcep_tlv_create_nopath_vector(uint32_t error_code)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR,
+ sizeof(struct pcep_object_tlv_nopath_vector));
+
+ tlv->error_code = error_code;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_vendor_info *
+pcep_tlv_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_specific_info)
+{
+ struct pcep_object_tlv_vendor_info *tlv =
+ (struct pcep_object_tlv_vendor_info *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_VENDOR_INFO,
+ sizeof(struct pcep_object_tlv_vendor_info));
+
+ tlv->enterprise_number = enterprise_number;
+ tlv->enterprise_specific_info = enterprise_specific_info;
+
+ return tlv;
+}
+
+/*
+ * SRPAG (SR Association Group) TLVs
+ */
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4)
+{
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ sizeof(struct pcep_object_tlv_srpag_pol_id));
+ tlv->color = color;
+ tlv->is_ipv4 = true;
+ memcpy(&tlv->end_point.ipv4.s_addr, ipv4, sizeof(struct in_addr));
+
+ return tlv;
+}
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6)
+{
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ sizeof(struct pcep_object_tlv_srpag_pol_id));
+ tlv->color = color;
+ tlv->is_ipv4 = false;
+ memcpy(&tlv->end_point.ipv6, ipv6, sizeof(struct in6_addr));
+
+ return tlv;
+}
+
+
+struct pcep_object_tlv_srpag_pol_name *
+pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length)
+{
+ if (pol_name == NULL) {
+ return NULL;
+ }
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME,
+ sizeof(struct pcep_object_tlv_srpag_pol_name));
+ uint16_t length =
+ (normalize_pcep_tlv_length(pol_name_length) > MAX_POLICY_NAME)
+ ? MAX_POLICY_NAME
+ : pol_name_length;
+ memcpy(tlv->name, pol_name, pol_name_length);
+ tlv->name_length = length;
+
+ return tlv;
+}
+struct pcep_object_tlv_srpag_cp_id *
+pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn,
+ struct in6_addr *in6_addr_with_mapped_ipv4,
+ uint32_t discriminator)
+{
+ if (!in6_addr_with_mapped_ipv4) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_srpag_cp_id *tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID,
+ sizeof(struct pcep_object_tlv_srpag_cp_id));
+ tlv->proto = proto_origin;
+ tlv->orig_asn = asn;
+ memcpy(&(tlv->orig_addres), in6_addr_with_mapped_ipv4,
+ sizeof(*in6_addr_with_mapped_ipv4));
+ tlv->discriminator = discriminator;
+
+ return tlv;
+}
+struct pcep_object_tlv_srpag_cp_pref *
+pcep_tlv_create_srpag_cp_pref(uint32_t pref)
+{
+
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE,
+ sizeof(struct pcep_object_tlv_srpag_cp_pref));
+ tlv->preference = pref;
+
+ return tlv;
+}
+
+struct pcep_object_tlv_arbitrary *
+pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length,
+ int tlv_id)
+{
+ if (data == NULL || data_length == 0) {
+ return NULL;
+ }
+
+ struct pcep_object_tlv_arbitrary *tlv =
+ (struct pcep_object_tlv_arbitrary *)pcep_tlv_common_create(
+ PCEP_OBJ_TLV_TYPE_ARBITRARY,
+ sizeof(struct pcep_object_tlv_arbitrary));
+
+ uint16_t length = (data_length > MAX_ARBITRARY_SIZE)
+ ? MAX_ARBITRARY_SIZE
+ : data_length;
+ memcpy(tlv->data, data, data_length);
+ tlv->data_length = length;
+ tlv->arbitraty_type = tlv_id;
+
+ return tlv;
+}
diff --git a/pceplib/pcep_msg_tlvs.h b/pceplib/pcep_msg_tlvs.h
new file mode 100644
index 0000000000..5197201e40
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs.h
@@ -0,0 +1,380 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+
+/*
+ * This is a High Level PCEP message object TLV API.
+ */
+
+#ifndef PCEP_TLVS_H_
+#define PCEP_TLVS_H_
+
+#include <arpa/inet.h>
+#include <stdint.h>
+
+#include "pcep.h"
+#include "pcep_utils_double_linked_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Regarding memory usage:
+ * When creating TLVs, any TLVs passed into messages or objects with these APIs
+ * will be free'd when the the enclosing pcep_message is free'd. That includes
+ * the double_linked_list's. So, just create the objects and TLVs, put them in
+ * their double_linked_list's, and everything will be managed internally. The
+ * enclosing message will be deleted by pcep_msg_free_message() or
+ * pcep_msg_free_message_list() which, * in turn will call one of:
+ * pcep_obj_free_object() and pcep_obj_free_tlv().
+ * For received messages, call pcep_msg_free_message() to free them.
+ */
+
+/* These numbers can be found here:
+ * https://www.iana.org/assignments/pcep/pcep.xhtml */
+enum pcep_object_tlv_types {
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1,
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
+ PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY =
+ 26, /* draft-ietf-pce-segment-routing-16 */
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY =
+ 34, /* RFC 8408, draft-ietf-pce-segment-routing-16 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID =
+ 60, /*TDB2 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME =
+ 61, /*TDB3 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID =
+ 62, /*TDB4 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE =
+ 63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */
+ PCEP_OBJ_TLV_TYPE_UNKNOWN = 128,
+ PCEP_OBJ_TLV_TYPE_ARBITRARY =
+ 65533 /* Max IANA To write arbitrary data */
+};
+
+struct pcep_object_tlv_header {
+ enum pcep_object_tlv_types type;
+ /* Pointer into encoded_message field from the pcep_message */
+ const uint8_t *encoded_tlv;
+ uint16_t encoded_tlv_length;
+};
+
+/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8231, 8232, 8281 */
+#define TLV_STATEFUL_PCE_CAP_FLAG_U 0x01
+#define TLV_STATEFUL_PCE_CAP_FLAG_S 0x02
+#define TLV_STATEFUL_PCE_CAP_FLAG_I 0x04
+#define TLV_STATEFUL_PCE_CAP_FLAG_T 0x08
+#define TLV_STATEFUL_PCE_CAP_FLAG_D 0x10
+#define TLV_STATEFUL_PCE_CAP_FLAG_F 0x20
+
+struct pcep_object_tlv_stateful_pce_capability {
+ struct pcep_object_tlv_header header;
+ bool flag_u_lsp_update_capability; /* RFC 8231 */
+ bool flag_s_include_db_version; /* RFC 8232 */
+ bool flag_i_lsp_instantiation_capability; /* RFC 8281 */
+ bool flag_t_triggered_resync; /* RFC 8232 */
+ bool flag_d_delta_lsp_sync; /* RFC 8232 */
+ bool flag_f_triggered_initial_sync; /* RFC 8232 */
+};
+
+/* NOPATH-VECTOR TLV, Used in the Reply NoPath Object. */
+struct pcep_object_tlv_nopath_vector {
+ struct pcep_object_tlv_header header;
+ uint32_t error_code;
+};
+
+/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8232 */
+struct pcep_object_tlv_lsp_db_version {
+ struct pcep_object_tlv_header header;
+ uint64_t lsp_db_version;
+};
+
+/* Speaker Entity Identifier TLV, Used in Open Object. RFCs: 8232 */
+struct pcep_object_tlv_speaker_entity_identifier {
+ struct pcep_object_tlv_header header;
+ double_linked_list *speaker_entity_id_list; /* list of uint32_t speaker
+ entity ids */
+};
+
+/* Ipv4 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */
+struct pcep_object_tlv_ipv4_lsp_identifier {
+ struct pcep_object_tlv_header header;
+ struct in_addr ipv4_tunnel_sender;
+ uint16_t lsp_id;
+ uint16_t tunnel_id;
+ struct in_addr extended_tunnel_id;
+ struct in_addr ipv4_tunnel_endpoint;
+};
+
+/* Ipv6 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */
+struct pcep_object_tlv_ipv6_lsp_identifier {
+ struct pcep_object_tlv_header header;
+ struct in6_addr ipv6_tunnel_sender;
+ uint16_t lsp_id;
+ uint16_t tunnel_id;
+ struct in6_addr extended_tunnel_id;
+ struct in6_addr ipv6_tunnel_endpoint;
+};
+
+/* Symbolic Path Name TLV, Used in LSP Object. RFCs: 8231 */
+#define MAX_SYMBOLIC_PATH_NAME 256
+
+struct pcep_object_tlv_symbolic_path_name {
+ struct pcep_object_tlv_header header;
+ uint16_t symbolic_path_name_length;
+ char symbolic_path_name[MAX_SYMBOLIC_PATH_NAME];
+};
+
+/* LSP Error Code TLV, Used in LSP Object. RFCs: 8231 */
+enum pcep_tlv_lsp_error_codes {
+ PCEP_TLV_LSP_ERROR_CODE_UNKNOWN = 1,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_LIMIT_REACHED = 2,
+ PCEP_TLV_LSP_ERROR_CODE_TOO_MANY_PENDING_LSP_UPDATES = 3,
+ PCEP_TLV_LSP_ERROR_CODE_UNACCEPTABLE_PARAMS = 4,
+ PCEP_TLV_LSP_ERROR_CODE_INTERNAL_ERROR = 5,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_BROUGHT_DOWN = 6,
+ PCEP_TLV_LSP_ERROR_CODE_LSP_PREEMPTED = 7,
+ PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR = 8,
+};
+
+struct pcep_object_tlv_lsp_error_code {
+ struct pcep_object_tlv_header header;
+ enum pcep_tlv_lsp_error_codes lsp_error_code;
+};
+
+/* Path Setup Type TLV, Used in RP and SRP Object. RFCs: 8408,
+ * draft-ietf-pce-segment-routing-16 */
+#define SR_TE_PST 1
+
+struct pcep_object_tlv_path_setup_type {
+ struct pcep_object_tlv_header header;
+ uint8_t path_setup_type;
+};
+
+/* Path Setup Type Capability TLV, Used in Open Object. RFCs: 8408,
+ * draft-ietf-pce-segment-routing-16 */
+struct pcep_object_tlv_path_setup_type_capability {
+ struct pcep_object_tlv_header header;
+ double_linked_list *pst_list; /* list of uint8_t PSTs */
+ double_linked_list *sub_tlv_list; /* list of sub_tlvs */
+};
+
+/* SR PCE Capability sub-TLV, Used in Open Object. RFCs:
+ * draft-ietf-pce-segment-routing-16 */
+#define TLV_SR_PCE_CAP_FLAG_X 0x01
+#define TLV_SR_PCE_CAP_FLAG_N 0x02
+
+struct pcep_object_tlv_sr_pce_capability {
+ struct pcep_object_tlv_header header;
+ bool flag_n;
+ bool flag_x;
+ uint8_t max_sid_depth;
+};
+
+
+/* RSVP Error Spec TLV, Used in LSP Object. RFCs: 8231, 2205 */
+#define RSVP_ERROR_SPEC_IPV4_CTYPE 1
+#define RSVP_ERROR_SPEC_IPV6_CTYPE 2
+#define RSVP_ERROR_SPEC_CLASS_NUM 6
+
+struct pcep_object_tlv_rsvp_error_spec {
+ struct pcep_object_tlv_header header;
+ uint8_t class_num;
+ uint8_t c_type;
+ uint8_t error_code;
+ uint16_t error_value;
+ /* Use the c_type to determine which union entry to use */
+ union error_spec_ip {
+ struct in_addr ipv4_error_node_address;
+ struct in6_addr ipv6_error_node_address;
+ } error_spec_ip;
+};
+
+/* SR Policy Identifier TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_pol_id {
+ struct pcep_object_tlv_header header;
+ uint32_t color;
+ bool is_ipv4;
+ union end_point_ {
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ } end_point;
+};
+
+/*draft-ietf-spring-segment-routing-policy-06*/
+#define MAX_POLICY_NAME 256
+
+/* SR Policy Name TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_pol_name {
+ struct pcep_object_tlv_header header;
+ uint16_t name_length;
+ char name[MAX_POLICY_NAME];
+};
+
+/* SR Candidate Path Id TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_cp_id {
+ struct pcep_object_tlv_header header;
+ uint8_t proto;
+ uint32_t orig_asn;
+ struct in6_addr orig_addres; /*With ipv4 embedded*/
+ uint32_t discriminator;
+};
+
+/* SR Candidate Preference TLV Used in Association Object.
+ * draft-barth-pce-segment-routing-policy-cp-04*/
+struct pcep_object_tlv_srpag_cp_pref {
+ struct pcep_object_tlv_header header;
+ uint32_t preference;
+};
+
+struct pcep_object_tlv_vendor_info {
+ struct pcep_object_tlv_header header;
+ uint32_t enterprise_number;
+ uint32_t enterprise_specific_info;
+};
+
+/* arbitrary TLV 65535 */
+#define MAX_ARBITRARY_SIZE 256
+struct pcep_object_tlv_arbitrary {
+ struct pcep_object_tlv_header header;
+ enum pcep_object_tlv_types arbitraty_type;
+ uint16_t data_length;
+ char data[MAX_ARBITRARY_SIZE];
+};
+
+/* Objective Functions List RFC 5541
+ * At least the following 6 OF codes must be supported */
+enum objective_function_codes {
+ PCEP_OF_CODE_MINIMUM_COST_PATH = 1, /* MCP */
+ PCEP_OF_CODE_MINIMUM_LOAD_PATH = 2, /* MLP */
+ PCEP_OF_CODE_MAXIMUM_BW_PATH = 3, /* MBP */
+ PCEP_OF_CODE_MINIMIZE_AGGR_BW_CONSUMPTION = 4, /* MBC */
+ PCEP_OF_CODE_MINIMIZE_MOST_LOADED_LINK = 5, /* MLL */
+ PCEP_OF_CODE_MINIMIZE_CUMULATIVE_COST_PATHS = 6, /* MCC */
+};
+
+struct pcep_object_tlv_of_list {
+ struct pcep_object_tlv_header header;
+ double_linked_list *of_list; /* list of uint16_t OF code points */
+};
+
+/*
+ * TLV creation functions
+ */
+
+/*
+ * Open Object TLVs
+ */
+
+struct pcep_object_tlv_stateful_pce_capability *
+pcep_tlv_create_stateful_pce_capability(
+ bool flag_u_lsp_update_capability, bool flag_s_include_db_version,
+ bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync,
+ bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync);
+struct pcep_object_tlv_lsp_db_version *
+pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version);
+struct pcep_object_tlv_speaker_entity_identifier *
+pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list);
+struct pcep_object_tlv_path_setup_type *
+pcep_tlv_create_path_setup_type(uint8_t pst);
+struct pcep_object_tlv_path_setup_type_capability *
+pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list,
+ double_linked_list *sub_tlv_list);
+struct pcep_object_tlv_sr_pce_capability *
+pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x,
+ uint8_t max_sid_depth);
+struct pcep_object_tlv_of_list *
+pcep_tlv_create_of_list(double_linked_list *of_list);
+
+/*
+ * LSP Object TLVs
+ */
+
+struct pcep_object_tlv_ipv4_lsp_identifier *
+pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender,
+ struct in_addr *ipv4_tunnel_endpoint,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in_addr *extended_tunnel_id);
+struct pcep_object_tlv_ipv6_lsp_identifier *
+pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender,
+ struct in6_addr *extended_tunnel_id,
+ uint16_t lsp_id, uint16_t tunnel_id,
+ struct in6_addr *ipv6_tunnel_endpoint);
+/* symbolic_path_name_length should NOT include the null terminator and cannot
+ * be zero */
+struct pcep_object_tlv_symbolic_path_name *
+pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name,
+ uint16_t symbolic_path_name_length);
+struct pcep_object_tlv_lsp_error_code *
+pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code);
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value);
+struct pcep_object_tlv_rsvp_error_spec *
+pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip,
+ uint8_t error_code, uint16_t error_value);
+
+struct pcep_object_tlv_nopath_vector *
+pcep_tlv_create_nopath_vector(uint32_t error_code);
+struct pcep_object_tlv_vendor_info *
+pcep_tlv_create_vendor_info(uint32_t enterprise_number,
+ uint32_t enterprise_specific_info);
+
+struct pcep_object_tlv_arbitrary *
+pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length,
+ int tlv_id);
+/*
+ * SRPAG (SR Association Group) TLVs
+ */
+
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4);
+struct pcep_object_tlv_srpag_pol_id *
+pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6);
+struct pcep_object_tlv_srpag_pol_name *
+pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length);
+struct pcep_object_tlv_srpag_cp_id *
+pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn,
+ struct in6_addr *in6_addr_with_mapped_ipv4,
+ uint32_t discriminator);
+struct pcep_object_tlv_srpag_cp_pref *
+pcep_tlv_create_srpag_cp_pref(uint32_t pref);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PCEP_TLVS_H_ */
diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c
new file mode 100644
index 0000000000..3322663dc3
--- /dev/null
+++ b/pceplib/pcep_msg_tlvs_encoding.c
@@ -0,0 +1,1282 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Encoding and decoding for PCEP Object TLVs.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr,
+ uint16_t tlv_length, struct pcep_versioning *versioning,
+ uint8_t *buf);
+void pcep_decode_tlv_hdr(const uint8_t *tlv_buf,
+ struct pcep_object_tlv_header *tlv_hdr);
+
+/*
+ * forward declarations for initialize_tlv_encoders()
+ */
+uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t
+pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+typedef uint16_t (*tlv_encoder_funcptr)(struct pcep_object_tlv_header *,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf);
+
+#define MAX_TLV_ENCODER_INDEX 65533 + 1 // 65
+
+#define PCEP_TLV_ENCODERS_ARGS \
+ struct pcep_object_tlv_header *, struct pcep_versioning *versioning, \
+ uint8_t *tlv_body_buf
+uint16_t (*const tlv_encoders[MAX_TLV_ENCODER_INDEX])(
+ PCEP_TLV_ENCODERS_ARGS) = {
+ [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_encode_tlv_no_path_vector,
+ [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_encode_tlv_stateful_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_encode_tlv_symbolic_path_name,
+ [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv4_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv6_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_encode_tlv_lsp_error_code,
+ [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_encode_tlv_rsvp_error_spec,
+ [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_encode_tlv_lsp_db_version,
+ [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_encode_tlv_speaker_entity_id,
+ [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_encode_tlv_sr_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_encode_tlv_path_setup_type,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_encode_tlv_path_setup_type_capability,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_encode_tlv_pol_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_encode_tlv_pol_name,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_encode_tlv_cpath_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_encode_tlv_cpath_preference,
+ [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_encode_tlv_vendor_info,
+ [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_encode_tlv_arbitrary,
+ [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_encode_tlv_of_list,
+};
+/*
+ * forward declarations for initialize_tlv_decoders()
+ */
+struct pcep_object_tlv_header *
+pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+struct pcep_object_tlv_header *
+pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf);
+typedef struct pcep_object_tlv_header *(*tlv_decoder_funcptr)(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf);
+
+// tlv_decoder_funcptr tlv_decoders[MAX_TLV_ENCODER_INDEX];
+
+#define PCEP_TLV_DECODERS_ARGS \
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf
+
+struct pcep_object_tlv_header *(*const tlv_decoders[MAX_TLV_ENCODER_INDEX])(
+ PCEP_TLV_DECODERS_ARGS) = {
+ [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_decode_tlv_no_path_vector,
+ [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_decode_tlv_stateful_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_decode_tlv_symbolic_path_name,
+ [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv4_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv6_lsp_identifiers,
+ [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_decode_tlv_lsp_error_code,
+ [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_decode_tlv_rsvp_error_spec,
+ [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_decode_tlv_lsp_db_version,
+ [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_decode_tlv_speaker_entity_id,
+ [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_decode_tlv_sr_pce_capability,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_decode_tlv_path_setup_type,
+ [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_decode_tlv_path_setup_type_capability,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_decode_tlv_pol_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_decode_tlv_pol_name,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_decode_tlv_cpath_id,
+ [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_decode_tlv_cpath_preference,
+ [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_decode_tlv_vendor_info,
+ [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_decode_tlv_arbitrary,
+ [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_decode_tlv_of_list,
+};
+
+static void initialize_tlv_coders()
+{
+ static bool initialized = false;
+
+ if (initialized == true) {
+ return;
+ }
+
+ initialized = true;
+
+ /* Encoders */
+ /*
+ memset(tlv_encoders, 0, sizeof(tlv_encoder_funcptr) *
+ MAX_TLV_ENCODER_INDEX); tlv_encoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] =
+ pcep_encode_tlv_no_path_vector;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_encode_tlv_stateful_pce_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_encode_tlv_symbolic_path_name;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv4_lsp_identifiers;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_encode_tlv_ipv6_lsp_identifiers;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] =
+ pcep_encode_tlv_lsp_error_code;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] =
+ pcep_encode_tlv_rsvp_error_spec;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] =
+ pcep_encode_tlv_lsp_db_version;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_encode_tlv_speaker_entity_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_encode_tlv_sr_pce_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] =
+ pcep_encode_tlv_path_setup_type;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_encode_tlv_path_setup_type_capability;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] =
+ pcep_encode_tlv_pol_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] =
+ pcep_encode_tlv_pol_name;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] =
+ pcep_encode_tlv_cpath_id;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_encode_tlv_cpath_preference;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] =
+ pcep_encode_tlv_vendor_info; tlv_encoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] =
+ pcep_encode_tlv_arbitrary;
+ tlv_encoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] =
+ pcep_encode_tlv_of_list;
+ */
+
+ /* Decoders */
+ /*
+ memset(tlv_decoders, 0, sizeof(tlv_decoder_funcptr) *
+ MAX_TLV_ENCODER_INDEX); tlv_decoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] =
+ pcep_decode_tlv_no_path_vector;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] =
+ pcep_decode_tlv_stateful_pce_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] =
+ pcep_decode_tlv_symbolic_path_name;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv4_lsp_identifiers;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] =
+ pcep_decode_tlv_ipv6_lsp_identifiers;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] =
+ pcep_decode_tlv_lsp_error_code;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] =
+ pcep_decode_tlv_rsvp_error_spec;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] =
+ pcep_decode_tlv_lsp_db_version;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] =
+ pcep_decode_tlv_speaker_entity_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] =
+ pcep_decode_tlv_sr_pce_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] =
+ pcep_decode_tlv_path_setup_type;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] =
+ pcep_decode_tlv_path_setup_type_capability;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] =
+ pcep_decode_tlv_pol_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] =
+ pcep_decode_tlv_pol_name;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] =
+ pcep_decode_tlv_cpath_id;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] =
+ pcep_decode_tlv_cpath_preference;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] =
+ pcep_decode_tlv_vendor_info; tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] =
+ pcep_decode_tlv_arbitrary;
+ tlv_decoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] =
+ pcep_decode_tlv_of_list;
+ */
+}
+
+uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr,
+ struct pcep_versioning *versioning, uint8_t *buf)
+{
+ initialize_tlv_coders();
+
+ if (tlv_hdr->type >= MAX_TLV_ENCODER_INDEX) {
+ pcep_log(LOG_INFO,
+ "%s: Cannot encode unknown Object class [%d]",
+ __func__, tlv_hdr->type);
+ return 0;
+ }
+
+ tlv_encoder_funcptr tlv_encoder = tlv_encoders[tlv_hdr->type];
+ if (tlv_encoder == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: No object encoder found for Object class [%d]",
+ __func__, tlv_hdr->type);
+ return 0;
+ }
+
+ /* Notice: The length in the TLV header does not include the TLV header,
+ * so the length returned from the tlv_encoder() is only the TLV body.
+ */
+ uint16_t tlv_length =
+ tlv_encoder(tlv_hdr, versioning, buf + TLV_HEADER_LENGTH);
+ write_tlv_header(tlv_hdr, tlv_length, versioning, buf);
+ tlv_hdr->encoded_tlv = buf;
+ tlv_hdr->encoded_tlv_length = tlv_length;
+
+ return normalize_pcep_tlv_length(tlv_length + TLV_HEADER_LENGTH);
+}
+
+/* TLV Header format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type (2 bytes) | Length (2 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value (Variable) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr,
+ uint16_t tlv_length, struct pcep_versioning *versioning,
+ uint8_t *buf)
+{
+ (void)versioning;
+ uint16_t *uint16_ptr = (uint16_t *)buf;
+ uint16_ptr[0] = htons(tlv_hdr->type);
+ uint16_ptr[1] = htons(tlv_length);
+}
+
+/*
+ * Functions to encode TLVs
+ */
+
+uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_nopath_vector *nopath_tlv =
+ (struct pcep_object_tlv_nopath_vector *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ *uint32_ptr = htonl(nopath_tlv->error_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t
+pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_stateful_pce_capability *spc_tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)tlv;
+ tlv_body_buf[3] =
+ ((spc_tlv->flag_f_triggered_initial_sync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_F
+ : 0x00)
+ | (spc_tlv->flag_d_delta_lsp_sync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_D
+ : 0x00)
+ | (spc_tlv->flag_t_triggered_resync == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_T
+ : 0x00)
+ | (spc_tlv->flag_i_lsp_instantiation_capability == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_I
+ : 0x00)
+ | (spc_tlv->flag_s_include_db_version == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_S
+ : 0x00)
+ | (spc_tlv->flag_u_lsp_update_capability == true
+ ? TLV_STATEFUL_PCE_CAP_FLAG_U
+ : 0x00));
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_symbolic_path_name *spn_tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ memcpy(tlv_body_buf, spn_tlv->symbolic_path_name,
+ spn_tlv->symbolic_path_name_length);
+
+ return spn_tlv->symbolic_path_name_length;
+}
+
+uint16_t
+pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = ipv4_lsp->ipv4_tunnel_sender.s_addr;
+ /* uint32_t[1] is lsp_id and tunnel_id, below */
+ uint32_ptr[2] = ipv4_lsp->extended_tunnel_id.s_addr;
+ uint32_ptr[3] = ipv4_lsp->ipv4_tunnel_endpoint.s_addr;
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD);
+ uint16_ptr[0] = htons(ipv4_lsp->lsp_id);
+ uint16_ptr[1] = htons(ipv4_lsp->tunnel_id);
+
+ return LENGTH_4WORDS;
+}
+
+uint16_t
+pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_ipv6_lsp_identifier *ipv6_lsp =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ encode_ipv6(&ipv6_lsp->ipv6_tunnel_sender, uint32_ptr);
+ encode_ipv6(&ipv6_lsp->extended_tunnel_id, uint32_ptr + 5);
+ encode_ipv6(&ipv6_lsp->ipv6_tunnel_endpoint, uint32_ptr + 9);
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS);
+ uint16_ptr[0] = htons(ipv6_lsp->lsp_id);
+ uint16_ptr[1] = htons(ipv6_lsp->tunnel_id);
+
+ return LENGTH_13WORDS;
+}
+
+uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_lsp_error_code *lsp_error_tlv =
+ (struct pcep_object_tlv_lsp_error_code *)tlv;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ *uint32_ptr = htonl(lsp_error_tlv->lsp_error_code);
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ /* Same decode tlv function for both types:
+ pcep_create_tlv_rsvp_ipv4_error_spec(tlv);
+ pcep_create_tlv_rsvp_ipv6_error_spec(tlv); */
+
+ /* RSVP Object Header
+ *
+ * 0 1 2 3
+ * +-------------+-------------+-------------+-------------+
+ * | Length (bytes) | Class-Num | C-Type |
+ * +-------------+-------------+-------------+-------------+
+ * | |
+ * // (Object contents) //
+ * | |
+ * +-------------+-------------+-------------+-------------+
+ *
+ * IPv4 ERROR_SPEC object: Class = 6, C-Type = 1
+ * +-------------+-------------+-------------+-------------+
+ * | IPv4 Error Node Address (4 bytes) |
+ * +-------------+-------------+-------------+-------------+
+ * | Flags | Error Code | Error Value |
+ * +-------------+-------------+-------------+-------------+
+ *
+ * IPv6 ERROR_SPEC object: Class = 6, C-Type = 2
+ * +-------------+-------------+-------------+-------------+
+ * | IPv6 Error Node Address (16 bytes) |
+ * +-------------+-------------+-------------+-------------+
+ * | Flags | Error Code | Error Value |
+ * +-------------+-------------+-------------+-------------+
+ */
+
+ (void)versioning;
+ struct pcep_object_tlv_rsvp_error_spec *rsvp_hdr =
+ (struct pcep_object_tlv_rsvp_error_spec *)tlv;
+ tlv_body_buf[2] = rsvp_hdr->class_num;
+ tlv_body_buf[3] = rsvp_hdr->c_type;
+
+ uint16_t *length_ptr = (uint16_t *)tlv_body_buf;
+ uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD);
+ if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV4_CTYPE) {
+ *length_ptr = htons(LENGTH_3WORDS);
+ *uint32_ptr =
+ rsvp_hdr->error_spec_ip.ipv4_error_node_address.s_addr;
+ tlv_body_buf[LENGTH_2WORDS + 1] = rsvp_hdr->error_code;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2);
+ *uint16_ptr = htons(rsvp_hdr->error_value);
+
+ return LENGTH_3WORDS;
+ } else if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV6_CTYPE) {
+ *length_ptr = htons(LENGTH_6WORDS);
+ encode_ipv6(&rsvp_hdr->error_spec_ip.ipv6_error_node_address,
+ uint32_ptr);
+ tlv_body_buf[LENGTH_5WORDS + 1] = rsvp_hdr->error_code;
+ uint16_t *uint16_ptr =
+ (uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2);
+ *uint16_ptr = htons(rsvp_hdr->error_value);
+
+ return LENGTH_6WORDS;
+ }
+
+ return 0;
+}
+
+uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_lsp_db_version *lsp_db_ver =
+ (struct pcep_object_tlv_lsp_db_version *)tlv;
+ *((uint64_t *)tlv_body_buf) = htobe64(lsp_db_ver->lsp_db_version);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_speaker_entity_identifier *speaker_id =
+ (struct pcep_object_tlv_speaker_entity_identifier *)tlv;
+ if (speaker_id->speaker_entity_id_list == NULL) {
+ return 0;
+ }
+
+ int index = 0;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ double_linked_list_node *node =
+ speaker_id->speaker_entity_id_list->head;
+ for (; node != NULL; node = node->next_node) {
+ uint32_ptr[index++] = htonl(*((uint32_t *)node->data));
+ }
+
+ return speaker_id->speaker_entity_id_list->num_entries * LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap =
+ (struct pcep_object_tlv_sr_pce_capability *)tlv;
+ tlv_body_buf[2] =
+ ((sr_pce_cap->flag_n == true ? TLV_SR_PCE_CAP_FLAG_N : 0x00)
+ | (sr_pce_cap->flag_x == true ? TLV_SR_PCE_CAP_FLAG_X : 0x00));
+ tlv_body_buf[3] = sr_pce_cap->max_sid_depth;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_path_setup_type *pst =
+ (struct pcep_object_tlv_path_setup_type *)tlv;
+ tlv_body_buf[3] = pst->path_setup_type;
+
+ return LENGTH_1WORD;
+}
+
+uint16_t
+pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_path_setup_type_capability *pst_cap =
+ (struct pcep_object_tlv_path_setup_type_capability *)tlv;
+ if (pst_cap->pst_list == NULL) {
+ return 0;
+ }
+
+ tlv_body_buf[3] = pst_cap->pst_list->num_entries;
+
+ /* Index past the reserved and NumPSTs fields */
+ int index = 4;
+ double_linked_list_node *node = pst_cap->pst_list->head;
+ for (; node != NULL; node = node->next_node) {
+ tlv_body_buf[index++] = *((uint8_t *)node->data);
+ }
+
+ uint16_t pst_length = normalize_pcep_tlv_length(
+ LENGTH_1WORD + pst_cap->pst_list->num_entries);
+ if (pst_cap->sub_tlv_list == NULL) {
+ return pst_length;
+ }
+
+ /* Any padding used for the PSTs should not be included in the tlv
+ * header length */
+ index = normalize_pcep_tlv_length(index);
+ uint16_t sub_tlvs_length = 0;
+ node = pst_cap->sub_tlv_list->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_tlv_header *sub_tlv =
+ (struct pcep_object_tlv_header *)node->data;
+ uint16_t sub_tlv_length = pcep_encode_tlv(sub_tlv, versioning,
+ tlv_body_buf + index);
+ index += sub_tlv_length;
+ sub_tlvs_length += sub_tlv_length;
+ }
+
+ return sub_tlvs_length + pst_length;
+}
+uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_pol_id *ipv4 =
+ (struct pcep_object_tlv_srpag_pol_id *)tlv;
+ if (ipv4->is_ipv4) {
+ uint32_ptr[0] = htonl(ipv4->color);
+ uint32_ptr[1] = ipv4->end_point.ipv4.s_addr;
+ return LENGTH_2WORDS;
+ } else {
+ struct pcep_object_tlv_srpag_pol_id *ipv6 =
+ (struct pcep_object_tlv_srpag_pol_id *)tlv;
+ uint32_ptr[0] = htonl(ipv6->color);
+ encode_ipv6(&ipv6->end_point.ipv6, &uint32_ptr[1]);
+ return LENGTH_5WORDS;
+ }
+}
+
+uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_pol_name *pol_name_tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)tlv;
+ memcpy(tlv_body_buf, pol_name_tlv->name, pol_name_tlv->name_length);
+
+ return normalize_pcep_tlv_length(pol_name_tlv->name_length);
+}
+
+uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_cp_id *cpath_id_tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv_body_buf[0] = cpath_id_tlv->proto;
+ uint32_ptr[1] = htonl(cpath_id_tlv->orig_asn);
+ encode_ipv6(&cpath_id_tlv->orig_addres, &uint32_ptr[2]);
+ uint32_ptr[6] = htonl(cpath_id_tlv->discriminator);
+
+ return sizeof(cpath_id_tlv->proto) + sizeof(cpath_id_tlv->orig_asn)
+ + sizeof(cpath_id_tlv->orig_addres)
+ + sizeof(cpath_id_tlv->discriminator);
+}
+
+uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_srpag_cp_pref *cpath_pref_tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = htonl(cpath_pref_tlv->preference);
+
+ return sizeof(cpath_pref_tlv->preference);
+}
+
+uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_vendor_info *vendor_info =
+ (struct pcep_object_tlv_vendor_info *)tlv;
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ uint32_ptr[0] = htonl(vendor_info->enterprise_number);
+ uint32_ptr[1] = htonl(vendor_info->enterprise_specific_info);
+
+ return LENGTH_2WORDS;
+}
+
+uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_arbitrary *tlv_arbitrary =
+ (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(tlv_body_buf, tlv_arbitrary->data, tlv_arbitrary->data_length);
+ tlv->type = tlv_arbitrary->arbitraty_type;
+
+ return tlv_arbitrary->data_length;
+}
+
+uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv,
+ struct pcep_versioning *versioning,
+ uint8_t *tlv_body_buf)
+{
+ (void)versioning;
+ struct pcep_object_tlv_of_list *of_list =
+ (struct pcep_object_tlv_of_list *)tlv;
+
+ if (of_list->of_list == NULL) {
+ return 0;
+ }
+
+ int index = 0;
+ double_linked_list_node *node = of_list->of_list->head;
+ while (node != NULL) {
+ uint16_t *of_code = (uint16_t *)node->data;
+ if (of_code == NULL) {
+ return 0;
+ }
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + index);
+ *uint16_ptr = *of_code;
+ index += 2;
+
+ node = node->next_node;
+ }
+
+ return of_list->of_list->num_entries * 2;
+}
+
+/*
+ * Decoding functions
+ */
+
+void pcep_decode_tlv_hdr(const uint8_t *tlv_buf,
+ struct pcep_object_tlv_header *tlv_hdr)
+{
+ memset(tlv_hdr, 0, sizeof(struct pcep_object_tlv_header));
+
+ uint16_t *uint16_ptr = (uint16_t *)tlv_buf;
+ tlv_hdr->type = ntohs(uint16_ptr[0]);
+ tlv_hdr->encoded_tlv_length = ntohs(uint16_ptr[1]);
+ tlv_hdr->encoded_tlv = tlv_buf;
+}
+
+struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf)
+{
+ initialize_tlv_coders();
+
+ struct pcep_object_tlv_header tlv_hdr;
+ /* Only initializes and decodes the Object Header: class, type, flags,
+ * and length */
+ pcep_decode_tlv_hdr(tlv_buf, &tlv_hdr);
+
+ if (tlv_hdr.type >= MAX_TLV_ENCODER_INDEX) {
+ pcep_log(LOG_INFO, "%s: Cannot decode unknown TLV type [%d]",
+ __func__, tlv_hdr.type);
+ return NULL;
+ }
+
+ tlv_decoder_funcptr tlv_decoder = tlv_decoders[tlv_hdr.type];
+ if (tlv_decoder == NULL) {
+ pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]",
+ __func__, tlv_hdr.type);
+ return NULL;
+ }
+
+ return tlv_decoder(&tlv_hdr, tlv_buf + LENGTH_1WORD);
+}
+
+static struct pcep_object_tlv_header *
+common_tlv_create(struct pcep_object_tlv_header *hdr, uint16_t new_tlv_length)
+{
+ struct pcep_object_tlv_header *new_tlv =
+ pceplib_malloc(PCEPLIB_MESSAGES, new_tlv_length);
+ memset(new_tlv, 0, new_tlv_length);
+ memcpy(new_tlv, hdr, sizeof(struct pcep_object_tlv_header));
+
+ return new_tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_nopath_vector));
+
+ tlv->error_code = ntohl(*((uint32_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_stateful_pce_capability));
+
+ tlv->flag_f_triggered_initial_sync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_F);
+ tlv->flag_d_delta_lsp_sync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_D);
+ tlv->flag_t_triggered_resync =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_T);
+ tlv->flag_i_lsp_instantiation_capability =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_I);
+ tlv->flag_s_include_db_version =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_S);
+ tlv->flag_u_lsp_update_capability =
+ (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_U);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ (struct pcep_object_tlv_symbolic_path_name *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_symbolic_path_name));
+
+ uint16_t length = tlv_hdr->encoded_tlv_length;
+ if (length > MAX_SYMBOLIC_PATH_NAME) {
+ /* TODO should we also reset the tlv_hdr->encoded_tlv_length ?
+ */
+ length = MAX_SYMBOLIC_PATH_NAME;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding Symbolic Path Name TLV, truncate path name from [%d] to [%d].\",",
+ __func__, tlv_hdr->encoded_tlv_length,
+ MAX_SYMBOLIC_PATH_NAME);
+ }
+
+ tlv->symbolic_path_name_length = length;
+ memcpy(tlv->symbolic_path_name, tlv_body_buf, length);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_ipv4_lsp_identifier));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->ipv4_tunnel_sender.s_addr = uint32_ptr[0];
+ /* uint32_t[1] is lsp_id and tunnel_id, below */
+ tlv->extended_tunnel_id.s_addr = uint32_ptr[2];
+ tlv->ipv4_tunnel_endpoint.s_addr = uint32_ptr[3];
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD);
+ tlv->lsp_id = ntohs(uint16_ptr[0]);
+ tlv->tunnel_id = ntohs(uint16_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ (struct pcep_object_tlv_ipv6_lsp_identifier *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_ipv6_lsp_identifier));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ decode_ipv6(uint32_ptr, &tlv->ipv6_tunnel_sender);
+ decode_ipv6(uint32_ptr + 5, &tlv->extended_tunnel_id);
+ decode_ipv6(uint32_ptr + 9, &tlv->ipv6_tunnel_endpoint);
+
+ uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS);
+ tlv->lsp_id = htons(uint16_ptr[0]);
+ tlv->tunnel_id = htons(uint16_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ (struct pcep_object_tlv_lsp_error_code *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_lsp_error_code));
+
+ tlv->lsp_error_code = ntohl(*((uint32_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint8_t class_num = tlv_body_buf[2];
+ uint8_t ctype = tlv_body_buf[3];
+
+ if (class_num != RSVP_ERROR_SPEC_CLASS_NUM) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding RSVP Error Spec TLV, unknown class num [%d]",
+ __func__, class_num);
+ return NULL;
+ }
+
+ if (ctype != RSVP_ERROR_SPEC_IPV4_CTYPE
+ && ctype != RSVP_ERROR_SPEC_IPV6_CTYPE) {
+ pcep_log(LOG_INFO,
+ "%s: Decoding RSVP Error Spec TLV, unknown ctype [%d]",
+ __func__, ctype);
+ return NULL;
+ }
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ (struct pcep_object_tlv_rsvp_error_spec *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_rsvp_error_spec));
+
+ tlv->class_num = class_num;
+ tlv->c_type = ctype;
+
+ uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD);
+ if (ctype == RSVP_ERROR_SPEC_IPV4_CTYPE) {
+ tlv->error_spec_ip.ipv4_error_node_address.s_addr = *uint32_ptr;
+ tlv->error_code = tlv_body_buf[LENGTH_2WORDS + 1];
+ tlv->error_value = ntohs(
+ *((uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2)));
+ } else /* RSVP_ERROR_SPEC_IPV6_CTYPE */
+ {
+ decode_ipv6(uint32_ptr,
+ &tlv->error_spec_ip.ipv6_error_node_address);
+ tlv->error_code = tlv_body_buf[LENGTH_5WORDS + 1];
+ tlv->error_value = ntohs(
+ *((uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2)));
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ (struct pcep_object_tlv_lsp_db_version *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_lsp_db_version));
+
+ tlv->lsp_db_version = be64toh(*((uint64_t *)tlv_body_buf));
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ (struct pcep_object_tlv_speaker_entity_identifier *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_speaker_entity_identifier));
+
+ uint8_t num_entity_ids = tlv_hdr->encoded_tlv_length / LENGTH_1WORD;
+ if (num_entity_ids > MAX_ITERATIONS) {
+ num_entity_ids = MAX_ITERATIONS;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode Speaker Entity ID, truncating num entities from [%d] to [%d].",
+ __func__, num_entity_ids, MAX_ITERATIONS);
+ }
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->speaker_entity_id_list = dll_initialize();
+ int i;
+ for (i = 0; i < num_entity_ids; i++) {
+ uint32_t *entity_id =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *entity_id = ntohl(uint32_ptr[i]);
+ dll_append(tlv->speaker_entity_id_list, entity_id);
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_sr_pce_capability));
+
+ tlv->flag_n = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_N);
+ tlv->flag_x = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_X);
+ tlv->max_sid_depth = tlv_body_buf[3];
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_path_setup_type *tlv =
+ (struct pcep_object_tlv_path_setup_type *)common_tlv_create(
+ tlv_hdr,
+ sizeof(struct pcep_object_tlv_path_setup_type));
+
+ tlv->path_setup_type = tlv_body_buf[3];
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability(
+ struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ (struct pcep_object_tlv_path_setup_type_capability *)
+ common_tlv_create(
+ tlv_hdr,
+ sizeof(struct
+ pcep_object_tlv_path_setup_type_capability));
+
+ uint8_t num_psts = tlv_body_buf[3];
+ if (num_psts > MAX_ITERATIONS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode Path Setup Type Capability num PSTs [%d] exceeds MAX [%d] continuing anyways",
+ __func__, num_psts, MAX_ITERATIONS);
+ }
+
+ int i;
+ tlv->pst_list = dll_initialize();
+ for (i = 0; i < num_psts; i++) {
+ uint8_t *pst =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
+ *pst = tlv_body_buf[i + LENGTH_1WORD];
+ dll_append(tlv->pst_list, pst);
+ }
+
+ if (tlv->header.encoded_tlv_length
+ == (TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts)) {
+ return (struct pcep_object_tlv_header *)tlv;
+ }
+
+ uint8_t num_iterations = 0;
+ tlv->sub_tlv_list = dll_initialize();
+ uint16_t buf_index = normalize_pcep_tlv_length(
+ TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts);
+ while ((tlv->header.encoded_tlv_length - buf_index) > TLV_HEADER_LENGTH
+ && num_iterations++ > MAX_ITERATIONS) {
+ struct pcep_object_tlv_header *sub_tlv =
+ pcep_decode_tlv(tlv_body_buf + buf_index);
+ if (sub_tlv == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Decode PathSetupType Capability sub-TLV decode returned NULL",
+ __func__);
+ return (struct pcep_object_tlv_header *)tlv;
+ }
+
+ buf_index +=
+ normalize_pcep_tlv_length(sub_tlv->encoded_tlv_length);
+ dll_append(tlv->sub_tlv_list, sub_tlv);
+ }
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_pol_id *ipv4 =
+ (struct pcep_object_tlv_srpag_pol_id *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_id));
+ if (tlv_hdr->encoded_tlv_length == 8) {
+ ipv4->is_ipv4 = true;
+ ipv4->color = ntohl(uint32_ptr[0]);
+ ipv4->end_point.ipv4.s_addr = uint32_ptr[1];
+ return (struct pcep_object_tlv_header *)ipv4;
+ } else {
+ ipv4->is_ipv4 = false;
+ struct pcep_object_tlv_srpag_pol_id *ipv6 =
+ (struct pcep_object_tlv_srpag_pol_id *)ipv4;
+ ipv6->color = ntohl(uint32_ptr[0]);
+ decode_ipv6(&uint32_ptr[1], &ipv6->end_point.ipv6);
+ return (struct pcep_object_tlv_header *)ipv6;
+ }
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ (struct pcep_object_tlv_srpag_pol_name *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_name));
+
+ memcpy(tlv->name, tlv_body_buf, tlv->header.encoded_tlv_length);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_cp_id *tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_id));
+
+ tlv->proto = tlv_body_buf[0];
+ tlv->orig_asn = ntohl(uint32_ptr[1]);
+ decode_ipv6(&uint32_ptr[2], &tlv->orig_addres);
+ tlv->discriminator = ntohl(uint32_ptr[6]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+struct pcep_object_tlv_header *
+pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_pref));
+
+ tlv->preference = ntohl(uint32_ptr[0]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_vendor_info *tlv =
+ (struct pcep_object_tlv_vendor_info *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_vendor_info));
+
+ uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf;
+ tlv->enterprise_number = ntohl(uint32_ptr[0]);
+ tlv->enterprise_specific_info = ntohl(uint32_ptr[1]);
+
+ return (struct pcep_object_tlv_header *)tlv;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_arbitrary *tlv_arbitrary =
+ (struct pcep_object_tlv_arbitrary *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_arbitrary));
+
+ uint16_t length = tlv_hdr->encoded_tlv_length;
+ if (length > MAX_ARBITRARY_SIZE) {
+ /* TODO should we also reset the tlv_hdr->encoded_tlv_length ?
+ */
+ length = MAX_ARBITRARY_SIZE;
+ pcep_log(
+ LOG_INFO,
+ "%s: Decoding Arbitrary TLV , truncate path name from [%d] to [%d].\",",
+ __func__, tlv_hdr->encoded_tlv_length,
+ MAX_ARBITRARY_SIZE);
+ }
+
+ tlv_arbitrary->data_length = length;
+ tlv_arbitrary->arbitraty_type = tlv_hdr->type;
+ tlv_hdr->type = PCEP_OBJ_TLV_TYPE_ARBITRARY;
+ memcpy(tlv_arbitrary->data, tlv_body_buf, length);
+
+ return (struct pcep_object_tlv_header *)tlv_arbitrary;
+}
+
+struct pcep_object_tlv_header *
+pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr,
+ const uint8_t *tlv_body_buf)
+{
+ struct pcep_object_tlv_of_list *of_tlv =
+ (struct pcep_object_tlv_of_list *)common_tlv_create(
+ tlv_hdr, sizeof(struct pcep_object_tlv_of_list));
+
+ of_tlv->of_list = dll_initialize();
+ uint16_t *uint16_ptr = (uint16_t *)tlv_body_buf;
+ int i = 0;
+ for (; i < tlv_hdr->encoded_tlv_length && i < MAX_ITERATIONS; i++) {
+ uint16_t *of_code_ptr =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint16_t));
+ *of_code_ptr = ntohs(uint16_ptr[i]);
+ dll_append(of_tlv->of_list, of_code_ptr);
+ }
+
+ return (struct pcep_object_tlv_header *)of_tlv;
+}
diff --git a/pceplib/pcep_msg_tools.c b/pceplib/pcep_msg_tools.c
new file mode 100644
index 0000000000..1d157ec3f5
--- /dev/null
+++ b/pceplib/pcep_msg_tools.c
@@ -0,0 +1,465 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_msg_tools.h"
+#include "pcep_msg_encoding.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+static const char *message_type_strs[] = {"NOT_IMPLEMENTED0",
+ "OPEN",
+ "KEEPALIVE",
+ "PCREQ",
+ "PCREP",
+ "PCNOTF",
+ "ERROR",
+ "CLOSE",
+ "NOT_IMPLEMENTED8",
+ "NOT_IMPLEMENTED9",
+ "REPORT",
+ "UPDATE",
+ "INITIATE",
+ "UNKOWN_MESSAGE_TYPE"};
+
+static const char *object_class_strs[] = {"NOT_IMPLEMENTED0",
+ "OPEN",
+ "RP",
+ "NOPATH",
+ "ENDPOINTS",
+ "BANDWIDTH",
+ "METRIC",
+ "ERO",
+ "RRO",
+ "LSPA",
+ "IRO",
+ "SVEC",
+ "NOTF",
+ "ERROR",
+ "NOT_IMPLEMENTED14",
+ "CLOSE",
+ "NOT_IMPLEMENTED16",
+ "NOT_IMPLEMENTED17",
+ "NOT_IMPLEMENTED18",
+ "NOT_IMPLEMENTED19",
+ "NOT_IMPLEMENTED20",
+ "OBJECTIVE_FUNCTION",
+ "NOT_IMPLEMENTED22",
+ "NOT_IMPLEMENTED23",
+ "NOT_IMPLEMENTED24",
+ "NOT_IMPLEMENTED25",
+ "NOT_IMPLEMENTED26",
+ "NOT_IMPLEMENTED27",
+ "NOT_IMPLEMENTED28",
+ "NOT_IMPLEMENTED29",
+ "NOT_IMPLEMENTED30",
+ "NOT_IMPLEMENTED31",
+ "LSP",
+ "SRP",
+ "VENDOR_INFO",
+ "NOT_IMPLEMENTED35",
+ "INTER_LAYER",
+ "SWITCH_LAYER",
+ "REQ_ADAP_CAP",
+ "SERVER_IND",
+ "ASSOCIATION", /* 40 */
+ "UNKNOWN_MESSAGE_TYPE"};
+
+
+double_linked_list *pcep_msg_read(int sock_fd)
+{
+ int ret;
+ uint8_t buffer[PCEP_MAX_SIZE] = {0};
+ uint16_t buffer_read = 0;
+
+
+ ret = read(sock_fd, &buffer, PCEP_MAX_SIZE);
+
+ if (ret < 0) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Failed to read from socket fd [%d] errno [%d %s]",
+ __func__, sock_fd, errno, strerror(errno));
+ return NULL;
+ } else if (ret == 0) {
+ pcep_log(LOG_INFO, "%s: pcep_msg_read: Remote shutdown fd [%d]",
+ __func__, sock_fd);
+ return NULL;
+ }
+
+ double_linked_list *msg_list = dll_initialize();
+ struct pcep_message *msg = NULL;
+
+ while ((ret - buffer_read) >= MESSAGE_HEADER_LENGTH) {
+
+ /* Get the Message header, validate it, and return the msg
+ * length */
+ int32_t msg_hdr_length =
+ pcep_decode_validate_msg_header(buffer + buffer_read);
+ if (msg_hdr_length < 0) {
+ /* If the message header is invalid, we cant keep
+ * reading since the length may be invalid */
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Received an invalid message fd [%d]",
+ __func__, sock_fd);
+ return msg_list;
+ }
+
+ /* Check if the msg_hdr_length is longer than what was read,
+ * in which case, we need to read the rest of the message. */
+ if ((ret - buffer_read) < msg_hdr_length) {
+ int read_len = (msg_hdr_length - (ret - buffer_read));
+ int read_ret = 0;
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Message not fully read! Trying to read %d bytes more, fd [%d]",
+ __func__, read_len, sock_fd);
+
+ read_ret = read(sock_fd, &buffer[ret], read_len);
+
+ if (read_ret != read_len) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_msg_read: Did not manage to read enough data (%d != %d) fd [%d]",
+ __func__, read_ret, read_len, sock_fd);
+ return msg_list;
+ }
+ }
+
+ msg = pcep_decode_message(buffer + buffer_read);
+ buffer_read += msg_hdr_length;
+
+ if (msg == NULL) {
+ return msg_list;
+ } else {
+ dll_append(msg_list, msg);
+ }
+ }
+
+ return msg_list;
+}
+
+struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type)
+{
+ if (msg_list == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = msg_list->head; node != NULL; node = node->next_node) {
+ if (((struct pcep_message *)node->data)->msg_header->type
+ == type) {
+ return (struct pcep_message *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_message *pcep_msg_get_next(double_linked_list *list,
+ struct pcep_message *current,
+ uint8_t type)
+{
+ if (list == NULL || current == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = list->head; node != NULL; node = node->next_node) {
+ if (node->data == current) {
+ continue;
+ }
+
+ if (((struct pcep_message *)node->data)->msg_header->type
+ == type) {
+ return (struct pcep_message *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *pcep_obj_get(double_linked_list *list,
+ uint8_t object_class)
+{
+ if (list == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *obj_item;
+ for (obj_item = list->head; obj_item != NULL;
+ obj_item = obj_item->next_node) {
+ if (((struct pcep_object_header *)obj_item->data)->object_class
+ == object_class) {
+ return (struct pcep_object_header *)obj_item->data;
+ }
+ }
+
+ return NULL;
+}
+
+struct pcep_object_header *pcep_obj_get_next(double_linked_list *list,
+ struct pcep_object_header *current,
+ uint8_t object_class)
+{
+ if (list == NULL || current == NULL) {
+ return NULL;
+ }
+
+ if (list->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *node;
+ for (node = list->head; node != NULL; node = node->next_node) {
+ if (node->data == current) {
+ continue;
+ }
+
+ if (((struct pcep_object_header *)node->data)->object_class
+ == object_class) {
+ return (struct pcep_object_header *)node->data;
+ }
+ }
+
+ return NULL;
+}
+
+void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv)
+{
+ /* Specific TLV freeing */
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ if (((struct pcep_object_tlv_speaker_entity_identifier *)tlv)
+ ->speaker_entity_id_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_speaker_entity_identifier *)
+ tlv)
+ ->speaker_entity_id_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ if (((struct pcep_object_tlv_path_setup_type_capability *)tlv)
+ ->pst_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_path_setup_type_capability *)
+ tlv)
+ ->pst_list,
+ PCEPLIB_MESSAGES);
+ }
+
+ if (((struct pcep_object_tlv_path_setup_type_capability *)tlv)
+ ->sub_tlv_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_object_tlv_path_setup_type_capability *)
+ tlv)
+ ->sub_tlv_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, tlv);
+}
+
+void pcep_obj_free_object(struct pcep_object_header *obj)
+{
+ /* Iterate the TLVs and free each one */
+ if (obj->tlv_list != NULL) {
+ struct pcep_object_tlv_header *tlv;
+ while ((tlv = (struct pcep_object_tlv_header *)
+ dll_delete_first_node(obj->tlv_list))
+ != NULL) {
+ pcep_obj_free_tlv(tlv);
+ }
+
+ dll_destroy(obj->tlv_list);
+ }
+
+ /* Specific object freeing */
+ switch (obj->object_class) {
+ case PCEP_OBJ_CLASS_ERO:
+ case PCEP_OBJ_CLASS_IRO:
+ case PCEP_OBJ_CLASS_RRO: {
+ if (((struct pcep_object_ro *)obj)->sub_objects != NULL) {
+ double_linked_list_node *node =
+ ((struct pcep_object_ro *)obj)
+ ->sub_objects->head;
+ for (; node != NULL; node = node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj =
+ (struct pcep_object_ro_subobj *)
+ node->data;
+ if (ro_subobj->ro_subobj_type
+ == RO_SUBOBJ_TYPE_SR) {
+ if (((struct pcep_ro_subobj_sr *)
+ ro_subobj)
+ ->nai_list
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct
+ pcep_ro_subobj_sr *)
+ ro_subobj)
+ ->nai_list,
+ PCEPLIB_MESSAGES);
+ }
+ }
+ }
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_ro *)obj)->sub_objects,
+ PCEPLIB_MESSAGES);
+ }
+ } break;
+
+ case PCEP_OBJ_CLASS_SVEC:
+ if (((struct pcep_object_svec *)obj)->request_id_list != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_svec *)obj)
+ ->request_id_list,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ case PCEP_OBJ_CLASS_SWITCH_LAYER:
+ if (((struct pcep_object_switch_layer *)obj)->switch_layer_rows
+ != NULL) {
+ dll_destroy_with_data_memtype(
+ ((struct pcep_object_switch_layer *)obj)
+ ->switch_layer_rows,
+ PCEPLIB_MESSAGES);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, obj);
+}
+
+void pcep_msg_free_message(struct pcep_message *message)
+{
+ /* Iterate the objects and free each one */
+ if (message->obj_list != NULL) {
+ struct pcep_object_header *obj;
+ while ((obj = (struct pcep_object_header *)
+ dll_delete_first_node(message->obj_list))
+ != NULL) {
+ pcep_obj_free_object(obj);
+ }
+
+ dll_destroy(message->obj_list);
+ }
+
+ if (message->msg_header != NULL) {
+ pceplib_free(PCEPLIB_MESSAGES, message->msg_header);
+ }
+
+ if (message->encoded_message != NULL) {
+ pceplib_free(PCEPLIB_MESSAGES, message->encoded_message);
+ }
+
+ pceplib_free(PCEPLIB_MESSAGES, message);
+}
+
+void pcep_msg_free_message_list(double_linked_list *list)
+{
+ /* Iterate the messages and free each one */
+ struct pcep_message *msg;
+ while ((msg = (struct pcep_message *)dll_delete_first_node(list))
+ != NULL) {
+ pcep_msg_free_message(msg);
+ }
+
+ dll_destroy(list);
+}
+
+const char *get_message_type_str(uint8_t type)
+{
+ uint8_t msg_type =
+ (type > PCEP_TYPE_INITIATE) ? PCEP_TYPE_INITIATE + 1 : type;
+
+ return message_type_strs[msg_type];
+}
+
+const char *get_object_class_str(uint8_t class)
+{
+ uint8_t object_class =
+ (class > PCEP_OBJ_CLASS_SRP) ? PCEP_OBJ_CLASS_SRP + 1 : class;
+
+ return object_class_strs[object_class];
+}
+
+/* Expecting a list of struct pcep_message pointers */
+void pcep_msg_print(double_linked_list *msg_list)
+{
+ double_linked_list_node *node;
+ for (node = msg_list->head; node != NULL; node = node->next_node) {
+ struct pcep_message *msg = (struct pcep_message *)node->data;
+ pcep_log(LOG_INFO, "%s: PCEP_MSG %s", __func__,
+ get_message_type_str(msg->msg_header->type));
+
+ double_linked_list_node *obj_node =
+ (msg->obj_list == NULL ? NULL : msg->obj_list->head);
+ for (; obj_node != NULL; obj_node = obj_node->next_node) {
+ struct pcep_object_header *obj_header =
+ ((struct pcep_object_header *)obj_node->data);
+ pcep_log(
+ LOG_INFO, "%s: PCEP_OBJ %s", __func__,
+ get_object_class_str(obj_header->object_class));
+ }
+ }
+}
+
+int pcep_msg_send(int sock_fd, struct pcep_message *msg)
+{
+ if (msg == NULL) {
+ return 0;
+ }
+
+ return write(sock_fd, msg->encoded_message,
+ ntohs(msg->encoded_message_length));
+}
diff --git a/pceplib/pcep_msg_tools.h b/pceplib/pcep_msg_tools.h
new file mode 100644
index 0000000000..b62bdde1cf
--- /dev/null
+++ b/pceplib/pcep_msg_tools.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ */
+
+#ifndef PCEP_TOOLS_H
+#define PCEP_TOOLS_H
+
+#include <stdint.h>
+#include <netinet/in.h> // struct in_addr
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PCEP_MAX_SIZE 6000
+
+/* Returns a double linked list of PCEP messages */
+double_linked_list *pcep_msg_read(int sock_fd);
+/* Given a double linked list of PCEP messages, return the first node that has
+ * the same message type */
+struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type);
+/* Given a double linked list of PCEP messages, return the next node after
+ * current node that has the same message type */
+struct pcep_message *pcep_msg_get_next(double_linked_list *msg_list,
+ struct pcep_message *current,
+ uint8_t type);
+struct pcep_object_header *pcep_obj_get(double_linked_list *list,
+ uint8_t object_class);
+struct pcep_object_header *pcep_obj_get_next(double_linked_list *list,
+ struct pcep_object_header *current,
+ uint8_t object_class);
+struct pcep_object_tlv_header *pcep_tlv_get(double_linked_list *list,
+ uint16_t type);
+struct pcep_object_tlv_header *
+pcep_tlv_get_next(double_linked_list *list,
+ struct pcep_object_tlv_header *current, uint16_t type);
+void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv);
+void pcep_obj_free_object(struct pcep_object_header *obj);
+void pcep_msg_free_message(struct pcep_message *message);
+void pcep_msg_free_message_list(double_linked_list *list);
+void pcep_msg_print(double_linked_list *list);
+const char *get_message_type_str(uint8_t type);
+const char *get_object_class_str(uint8_t class);
+int pcep_msg_send(int sock_fd, struct pcep_message *hdr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pceplib/pcep_pcc.c b/pceplib/pcep_pcc.c
new file mode 100644
index 0000000000..2171f883cd
--- /dev/null
+++ b/pceplib/pcep_pcc.c
@@ -0,0 +1,517 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Sample PCC implementation
+ */
+
+#include <zebra.h>
+
+#include <netdb.h> // gethostbyname
+#include <netinet/tcp.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pcep_pcc_api.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/*
+ * PCEP PCC design spec:
+ * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
+ */
+#define MAX_SRC_IP_STR 40
+#define MAX_DST_IP_STR 40
+struct cmd_line_args {
+ char src_ip_str[MAX_SRC_IP_STR];
+ char dest_ip_str[MAX_DST_IP_STR];
+ short src_tcp_port;
+ short dest_tcp_port;
+ char tcp_md5_str[TCP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */
+ bool is_ipv6;
+ bool eventpoll; /* poll for pcep_event's, or use callback (default) */
+};
+
+bool pcc_active_ = true;
+pcep_session *session = NULL;
+struct cmd_line_args *cmd_line_args = NULL;
+/* pcep_event callback variables */
+bool pcep_event_condition = false;
+struct pcep_event *event = NULL;
+pthread_mutex_t pcep_event_mutex;
+pthread_cond_t pcep_event_cond_var;
+
+static const char DEFAULT_DEST_HOSTNAME[] = "localhost";
+static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost";
+static const short DEFAULT_SRC_TCP_PORT = 4999;
+
+// Private fn's
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[]);
+void handle_signal_action(int sig_number);
+int setup_signals(void);
+void send_pce_path_request_message(pcep_session *session);
+void send_pce_report_message(pcep_session *session);
+void print_queue_event(struct pcep_event *event);
+void pcep_event_callback(void *cb_data, pcep_event *e);
+
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[])
+{
+ /* Allocate and set default values */
+ struct cmd_line_args *cmd_line_args =
+ malloc(sizeof(struct cmd_line_args));
+ memset(cmd_line_args, 0, sizeof(struct cmd_line_args));
+ strlcpy(cmd_line_args->dest_ip_str, DEFAULT_DEST_HOSTNAME,
+ MAX_DST_IP_STR);
+ cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT;
+ cmd_line_args->is_ipv6 = false;
+
+ /* Parse the cmd_line args:
+ * -ipv6
+ * -srcip localhost
+ * -destip 192.168.0.2
+ * -srcport 4999
+ * -dstport 4189
+ * -tcpmd5 hello
+ * -event_poll */
+ int i = 1;
+ for (; i < argc; ++i) {
+ if (strcmp(argv[i], "-help") == 0
+ || strcmp(argv[i], "--help") == 0
+ || strcmp(argv[i], "-h") == 0) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ } else if (strcmp(argv[i], "-ipv6") == 0) {
+ cmd_line_args->is_ipv6 = true;
+ if (argc == 2) {
+ strlcpy(cmd_line_args->dest_ip_str,
+ DEFAULT_DEST_HOSTNAME_IPV6,
+ MAX_DST_IP_STR);
+ }
+ } else if (strcmp(argv[i], "-eventpoll") == 0) {
+ cmd_line_args->eventpoll = true;
+ } else if (strcmp(argv[i], "-srcip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->src_ip_str, argv[++i],
+ MAX_SRC_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->dest_ip_str, argv[++i],
+ MAX_DST_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-srcport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->src_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->dest_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-tcpmd5") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->tcp_md5_str, argv[++i],
+ sizeof(cmd_line_args->tcp_md5_str));
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else {
+ pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s",
+ __func__, i, argv[i]);
+ free(cmd_line_args);
+ return NULL;
+ }
+ }
+
+ return cmd_line_args;
+}
+
+void handle_signal_action(int sig_number)
+{
+ if (sig_number == SIGINT) {
+ pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__);
+ pcc_active_ = false;
+ if (cmd_line_args->eventpoll == false) {
+ pthread_mutex_lock(&pcep_event_mutex);
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+ } else if (sig_number == SIGUSR1) {
+ pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters",
+ __func__);
+ dump_pcep_session_counters(session);
+ pceplib_memory_dump();
+ } else if (sig_number == SIGUSR2) {
+ pcep_log(LOG_INFO, "%s: SIGUSR2 was caught, reseting counters",
+ __func__);
+ reset_pcep_session_counters(session);
+ }
+}
+
+
+int setup_signals()
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = handle_signal_action;
+ if (sigaction(SIGINT, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR1, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR2, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ return 0;
+}
+
+void send_pce_path_request_message(pcep_session *session)
+{
+ struct in_addr src_ipv4;
+ struct in_addr dst_ipv4;
+ inet_pton(AF_INET, "1.2.3.4", &src_ipv4);
+ inet_pton(AF_INET, "10.20.30.40", &dst_ipv4);
+
+ struct pcep_object_rp *rp_object =
+ pcep_obj_create_rp(1, false, false, false, false, 42, NULL);
+ struct pcep_object_endpoints_ipv4 *ep_object =
+ pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4);
+
+ struct pcep_message *path_request =
+ pcep_msg_create_request(rp_object, ep_object, NULL);
+ send_message(session, path_request, true);
+}
+
+void send_pce_report_message(pcep_session *session)
+{
+ double_linked_list *report_list = dll_initialize();
+
+ /* SRP Path Setup Type TLV */
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ pcep_tlv_create_path_setup_type(SR_TE_PST);
+ double_linked_list *srp_tlv_list = dll_initialize();
+ dll_append(srp_tlv_list, pst_tlv);
+
+ /*
+ * Create the SRP object
+ */
+ uint32_t srp_id_number = 0x10203040;
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)pcep_obj_create_srp(
+ false, srp_id_number, srp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message SRP object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* LSP Symbolic path name TLV */
+ char symbolic_path_name[] = "second-default";
+ struct pcep_object_tlv_symbolic_path_name *spn_tlv =
+ pcep_tlv_create_symbolic_path_name(symbolic_path_name, 14);
+ double_linked_list *lsp_tlv_list = dll_initialize();
+ dll_append(lsp_tlv_list, spn_tlv);
+
+ /* LSP IPv4 LSP ID TLV */
+ struct in_addr ipv4_tunnel_sender;
+ struct in_addr ipv4_tunnel_endpoint;
+ inet_pton(AF_INET, "9.9.1.1", &ipv4_tunnel_sender);
+ inet_pton(AF_INET, "9.9.2.1", &ipv4_tunnel_endpoint);
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id_tlv =
+ pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender,
+ &ipv4_tunnel_endpoint, 42,
+ 1, NULL);
+ dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
+
+ /*
+ * Create the LSP object
+ */
+ uint32_t plsp_id = 42;
+ enum pcep_lsp_operational_status lsp_status =
+ PCEP_LSP_OPERATIONAL_ACTIVE;
+ bool c_flag = false; /* Lsp was created by PcInitiate msg */
+ bool a_flag = false; /* Admin state, active / inactive */
+ bool r_flag = false; /* true if LSP has been removed */
+ bool s_flag = true; /* Synchronization */
+ bool d_flag = false; /* Delegate LSP to PCE */
+ obj = (struct pcep_object_header *)pcep_obj_create_lsp(
+ plsp_id, lsp_status, c_flag, a_flag, r_flag, s_flag, d_flag,
+ lsp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message LSP object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* Create 2 ERO NONAI sub-objects */
+ double_linked_list *ero_subobj_list = dll_initialize();
+ struct pcep_ro_subobj_sr *sr_subobj_nonai1 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai1);
+
+ struct pcep_ro_subobj_sr *sr_subobj_nonai2 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai2);
+
+ /* Create ERO IPv4 node sub-object */
+ struct in_addr sr_subobj_ipv4;
+ inet_pton(AF_INET, "9.9.9.1", &sr_subobj_ipv4);
+ struct pcep_ro_subobj_sr *sr_subobj_ipv4node =
+ pcep_obj_create_ro_subobj_sr_ipv4_node(
+ false, false, false, true, 16060, &sr_subobj_ipv4);
+ if (sr_subobj_ipv4node == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO sub-object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(ero_subobj_list, sr_subobj_ipv4node);
+
+ /*
+ * Create the ERO object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /*
+ * Create the Metric object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_metric(
+ PCEP_METRIC_TE, false, true, 16.0);
+ dll_append(report_list, obj);
+
+ /* Create and send the report message */
+ struct pcep_message *report_msg = pcep_msg_create_report(report_list);
+ send_message(session, report_msg, true);
+}
+
+void print_queue_event(struct pcep_event *event)
+{
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
+ __func__, time(NULL), pthread_self(),
+ get_event_type_str(event->event_type),
+ event->session->session_id, event->event_time);
+
+ if (event->event_type == MESSAGE_RECEIVED) {
+ pcep_log(
+ LOG_INFO, "%s: \t Event message type [%s]", __func__,
+ get_message_type_str(event->message->msg_header->type));
+ }
+}
+
+/* Called by pcep_session_logic when pcep_event's are ready */
+void pcep_event_callback(void *cb_data, pcep_event *e)
+{
+ (void)cb_data;
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
+ time(NULL), pthread_self());
+ pthread_mutex_lock(&pcep_event_mutex);
+ event = e;
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+}
+
+int main(int argc, char **argv)
+{
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
+ __func__, time(NULL), pthread_self());
+
+ cmd_line_args = get_cmdline_args(argc, argv);
+ if (cmd_line_args == NULL) {
+ return -1;
+ }
+
+ setup_signals();
+
+ if (cmd_line_args->eventpoll == false) {
+ struct pceplib_infra_config infra_config;
+ memset(&infra_config, 0, sizeof(infra_config));
+ infra_config.pcep_event_func = pcep_event_callback;
+ if (!initialize_pcc_infra(&infra_config)) {
+ pcep_log(LOG_ERR,
+ "%s: Error initializing PCC with infra.",
+ __func__);
+ return -1;
+ }
+ } else {
+ if (!initialize_pcc()) {
+ pcep_log(LOG_ERR, "%s: Error initializing PCC.",
+ __func__);
+ return -1;
+ }
+ }
+
+ pcep_configuration *config = create_default_pcep_configuration();
+ config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true;
+ config->src_pcep_port = cmd_line_args->src_tcp_port;
+ config->is_tcp_auth_md5 = true;
+
+ strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
+ sizeof(config->tcp_authentication_str));
+
+ int af = (cmd_line_args->is_ipv6 ? AF_INET6 : AF_INET);
+ struct hostent *host_info =
+ gethostbyname2(cmd_line_args->dest_ip_str, af);
+ if (host_info == NULL) {
+ pcep_log(LOG_ERR, "%s: Error getting IP address.", __func__);
+ return -1;
+ }
+
+ if (cmd_line_args->is_ipv6) {
+ struct in6_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce_ipv6(config, &host_address);
+ } else {
+ struct in_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce(config, &host_address);
+ }
+
+ if (session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
+ destroy_pcep_configuration(config);
+ return -1;
+ }
+
+ sleep(2);
+
+ send_pce_report_message(session);
+ /*send_pce_path_request_message(session);*/
+
+ /* Wait for pcep_event's either by polling the event queue or by
+ * callback */
+ if (cmd_line_args->eventpoll == true) {
+ /* Poll the pcep_event queue*/
+ while (pcc_active_) {
+ if (event_queue_is_empty() == false) {
+ struct pcep_event *event =
+ event_queue_get_event();
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ sleep(5);
+ }
+ } else {
+ /* Get events via callback and conditional variable */
+ pthread_mutex_init(&pcep_event_mutex, NULL);
+ pthread_cond_init(&pcep_event_cond_var, NULL);
+ while (pcc_active_) {
+ pthread_mutex_lock(&pcep_event_mutex);
+
+ /* this internal loop helps avoid spurious interrupts */
+ while (!pcep_event_condition) {
+ pthread_cond_wait(&pcep_event_cond_var,
+ &pcep_event_mutex);
+ }
+
+ /* Check if we have been interrupted by SIGINT */
+ if (pcc_active_) {
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ pcep_event_condition = false;
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+
+ pthread_mutex_destroy(&pcep_event_mutex);
+ pthread_cond_destroy(&pcep_event_cond_var);
+ }
+
+ pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
+ disconnect_pce(session);
+ destroy_pcep_configuration(config);
+ free(cmd_line_args);
+
+ if (!destroy_pcc()) {
+ pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
+ }
+
+ pceplib_memory_dump();
+
+ return 0;
+}
diff --git a/pceplib/pcep_pcc_api.c b/pceplib/pcep_pcc_api.c
new file mode 100644
index 0000000000..b7813c5a05
--- /dev/null
+++ b/pceplib/pcep_pcc_api.c
@@ -0,0 +1,392 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public PCEPlib PCC API implementation
+ */
+
+#include <zebra.h>
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_msg_messages.h"
+#include "pcep_pcc_api.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+
+/* Not using an array here since the enum pcep_event_type indeces go into the
+ * 100's */
+const char MESSAGE_RECEIVED_STR[] = "MESSAGE_RECEIVED";
+const char PCE_CLOSED_SOCKET_STR[] = "PCE_CLOSED_SOCKET";
+const char PCE_SENT_PCEP_CLOSE_STR[] = "PCE_SENT_PCEP_CLOSE";
+const char PCE_DEAD_TIMER_EXPIRED_STR[] = "PCE_DEAD_TIMER_EXPIRED";
+const char PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR[] =
+ "PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED";
+const char PCC_CONNECTED_TO_PCE_STR[] = "PCC_CONNECTED_TO_PCE";
+const char PCC_PCEP_SESSION_CLOSED_STR[] = "PCC_PCEP_SESSION_CLOSED";
+const char PCC_RCVD_INVALID_OPEN_STR[] = "PCC_RCVD_INVALID_OPEN";
+const char PCC_RCVD_MAX_INVALID_MSGS_STR[] = "PCC_RCVD_MAX_INVALID_MSGS";
+const char PCC_RCVD_MAX_UNKOWN_MSGS_STR[] = "PCC_RCVD_MAX_UNKOWN_MSGS";
+const char UNKNOWN_EVENT_STR[] = "UNKNOWN Event Type";
+
+/* Session Logic Handle managed in pcep_session_logic.c */
+extern pcep_event_queue *session_logic_event_queue_;
+
+bool initialize_pcc()
+{
+ if (!run_session_logic()) {
+ pcep_log(LOG_ERR, "%s: Error initializing PCC session logic.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool initialize_pcc_infra(struct pceplib_infra_config *infra_config)
+{
+ if (infra_config == NULL) {
+ return initialize_pcc();
+ }
+
+ if (!run_session_logic_with_infra(infra_config)) {
+ pcep_log(LOG_ERR,
+ "%s: Error initializing PCC session logic with infra.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+/* this function is blocking */
+bool initialize_pcc_wait_for_completion()
+{
+ return run_session_logic_wait_for_completion();
+}
+
+
+bool destroy_pcc()
+{
+ if (!stop_session_logic()) {
+ pcep_log(LOG_WARNING, "%s: Error stopping PCC session logic.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+pcep_configuration *create_default_pcep_configuration()
+{
+ pcep_configuration *config =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_configuration));
+ memset(config, 0, sizeof(pcep_configuration));
+
+ config->keep_alive_seconds = DEFAULT_CONFIG_KEEP_ALIVE;
+ /* This value will possibly be overwritten later with PCE config data */
+ config->keep_alive_pce_negotiated_timer_seconds =
+ DEFAULT_CONFIG_KEEP_ALIVE;
+ config->min_keep_alive_seconds = DEFAULT_MIN_CONFIG_KEEP_ALIVE;
+ config->max_keep_alive_seconds = DEFAULT_MAX_CONFIG_KEEP_ALIVE;
+
+ config->dead_timer_seconds = DEFAULT_CONFIG_DEAD_TIMER;
+ /* This value will be overwritten later with PCE config data */
+ config->dead_timer_pce_negotiated_seconds = DEFAULT_CONFIG_DEAD_TIMER;
+ config->min_dead_timer_seconds = DEFAULT_MIN_CONFIG_DEAD_TIMER;
+ config->max_dead_timer_seconds = DEFAULT_MAX_CONFIG_DEAD_TIMER;
+
+ config->request_time_seconds = DEFAULT_CONFIG_REQUEST_TIME;
+ config->max_unknown_messages = DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES;
+ config->max_unknown_requests = DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS;
+
+ config->socket_connect_timeout_millis =
+ DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS;
+ config->support_stateful_pce_lsp_update = true;
+ config->support_pce_lsp_instantiation = true;
+ config->support_include_db_version = true;
+ config->lsp_db_version = 0;
+ config->support_lsp_triggered_resync = true;
+ config->support_lsp_delta_sync = true;
+ config->support_pce_triggered_initial_sync = true;
+ config->support_sr_te_pst = true;
+ config->pcc_can_resolve_nai_to_sid = true;
+ config->max_sid_depth = 0;
+ config->dst_pcep_port = 0;
+ config->src_pcep_port = 0;
+ config->src_ip.src_ipv4.s_addr = INADDR_ANY;
+ config->is_src_ipv6 = false;
+ config->pcep_msg_versioning = create_default_pcep_versioning();
+ config->tcp_authentication_str[0] = '\0';
+ config->is_tcp_auth_md5 = true;
+
+ return config;
+}
+
+void destroy_pcep_configuration(pcep_configuration *config)
+{
+ destroy_pcep_versioning(config->pcep_msg_versioning);
+ pceplib_free(PCEPLIB_INFRA, config);
+}
+
+pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip)
+{
+ return create_pcep_session(config, pce_ip);
+}
+
+pcep_session *connect_pce_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip)
+{
+ return create_pcep_session_ipv6(config, pce_ip);
+}
+
+void disconnect_pce(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: disconnect_pce session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ if (session->socket_comm_session == NULL
+ || session->socket_comm_session->socket_fd < 0) {
+ /* If the socket has already been closed, just destroy the
+ * session */
+ destroy_pcep_session(session);
+ } else {
+ /* This will cause the session to be destroyed AFTER the close
+ * message is sent */
+ session->destroy_session_after_write = true;
+
+ /* Send a PCEP close message */
+ close_pcep_session(session);
+ }
+}
+
+void send_message(pcep_session *session, struct pcep_message *msg,
+ bool free_after_send)
+{
+ if (session == NULL || msg == NULL) {
+ pcep_log(LOG_DEBUG,
+ "%s: send_message NULL params session [%p] msg [%p]",
+ __func__, session, msg);
+
+ return;
+ }
+
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: send_message session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ pcep_encode_message(msg, session->pcc_config.pcep_msg_versioning);
+ socket_comm_session_send_message(
+ session->socket_comm_session, (char *)msg->encoded_message,
+ msg->encoded_message_length, free_after_send);
+
+ increment_message_tx_counters(session, msg);
+
+ if (free_after_send == true) {
+ /* The encoded_message will be deleted once sent, so everything
+ * else in the message will be freed */
+ msg->encoded_message = NULL;
+ pcep_msg_free_message(msg);
+ }
+}
+
+/* Returns true if the queue is empty, false otherwise */
+bool event_queue_is_empty()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_is_empty Session Logic is not initialized yet",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ bool is_empty =
+ (session_logic_event_queue_->event_queue->num_entries == 0);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return is_empty;
+}
+
+
+/* Return the number of events on the queue, 0 if empty */
+uint32_t event_queue_num_events_available()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_num_events_available Session Logic is not initialized yet",
+ __func__);
+ return 0;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ uint32_t num_events =
+ session_logic_event_queue_->event_queue->num_entries;
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return num_events;
+}
+
+
+/* Return the next event on the queue, NULL if empty */
+struct pcep_event *event_queue_get_event()
+{
+ if (session_logic_event_queue_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: event_queue_get_event Session Logic is not initialized yet",
+ __func__);
+ return NULL;
+ }
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ struct pcep_event *event = (struct pcep_event *)queue_dequeue(
+ session_logic_event_queue_->event_queue);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ return event;
+}
+
+
+/* Free the PCEP Event resources, including the PCEP message */
+void destroy_pcep_event(struct pcep_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: destroy_pcep_event cannot destroy NULL event",
+ __func__);
+ return;
+ }
+
+ if (event->event_type == MESSAGE_RECEIVED && event->message != NULL) {
+ pcep_msg_free_message(event->message);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, event);
+}
+
+const char *get_event_type_str(int event_type)
+{
+ switch (event_type) {
+ case MESSAGE_RECEIVED:
+ return MESSAGE_RECEIVED_STR;
+ break;
+ case PCE_CLOSED_SOCKET:
+ return PCE_CLOSED_SOCKET_STR;
+ break;
+ case PCE_SENT_PCEP_CLOSE:
+ return PCE_SENT_PCEP_CLOSE_STR;
+ break;
+ case PCE_DEAD_TIMER_EXPIRED:
+ return PCE_DEAD_TIMER_EXPIRED_STR;
+ break;
+ case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED:
+ return PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR;
+ break;
+ case PCC_CONNECTED_TO_PCE:
+ return PCC_CONNECTED_TO_PCE_STR;
+ break;
+ case PCC_PCEP_SESSION_CLOSED:
+ return PCC_PCEP_SESSION_CLOSED_STR;
+ break;
+ case PCC_RCVD_INVALID_OPEN:
+ return PCC_RCVD_INVALID_OPEN_STR;
+ break;
+ case PCC_RCVD_MAX_INVALID_MSGS:
+ return PCC_RCVD_MAX_INVALID_MSGS_STR;
+ break;
+ case PCC_RCVD_MAX_UNKOWN_MSGS:
+ return PCC_RCVD_MAX_UNKOWN_MSGS_STR;
+ break;
+ default:
+ return UNKNOWN_EVENT_STR;
+ break;
+ }
+}
+
+void dump_pcep_session_counters(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: dump_pcep_session_counters session [%p] has already been deleted",
+ __func__, session);
+ return;
+ }
+
+ /* Update the counters group name so that the PCE session connected time
+ * is accurate */
+ time_t now = time(NULL);
+ char counters_name[MAX_COUNTER_STR_LENGTH] = {0};
+ char ip_str[40] = {0};
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ ip_str, 40);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ ip_str, 40);
+ }
+ snprintf(counters_name, MAX_COUNTER_STR_LENGTH,
+ "PCEP Session [%d], connected to [%s] for [%u seconds]",
+ session->session_id, ip_str,
+ (uint32_t)(now - session->time_connected));
+ strlcpy(session->pcep_session_counters->counters_group_name,
+ counters_name,
+ sizeof(session->pcep_session_counters->counters_group_name));
+
+ dump_counters_group_to_log(session->pcep_session_counters);
+}
+
+void reset_pcep_session_counters(pcep_session *session)
+{
+ if (session_exists(session) == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: reset_pcep_session_counters session [%p] has already been deleted",
+ session);
+ return;
+ }
+
+ reset_group_counters(session->pcep_session_counters);
+}
diff --git a/pceplib/pcep_pcc_api.h b/pceplib/pcep_pcc_api.h
new file mode 100644
index 0000000000..5756e23efc
--- /dev/null
+++ b/pceplib/pcep_pcc_api.h
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public PCEPlib PCC API
+ */
+
+#ifndef PCEPPCC_INCLUDE_PCEPPCCAPI_H_
+#define PCEPPCC_INCLUDE_PCEPPCCAPI_H_
+
+#include <stdbool.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_timers.h"
+
+#define DEFAULT_PCEP_TCP_PORT 4189
+#define DEFAULT_CONFIG_KEEP_ALIVE 30
+#define DEFAULT_CONFIG_DEAD_TIMER DEFAULT_CONFIG_KEEP_ALIVE * 4
+#define DEFAULT_CONFIG_REQUEST_TIME 30
+#define DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS 5
+#define DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES 5
+#define DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS 250
+
+/* Acceptable MIN and MAX values used in deciding if the PCEP
+ * Open received from a PCE should be accepted or rejected. */
+#define DEFAULT_MIN_CONFIG_KEEP_ALIVE 5
+#define DEFAULT_MAX_CONFIG_KEEP_ALIVE 120
+#define DEFAULT_MIN_CONFIG_DEAD_TIMER DEFAULT_MIN_CONFIG_KEEP_ALIVE * 4
+#define DEFAULT_MAX_CONFIG_DEAD_TIMER DEFAULT_MAX_CONFIG_KEEP_ALIVE * 4
+
+/*
+ * PCEP PCC library initialization/teardown functions
+ */
+
+/* Later when this is integrated with FRR pathd, it will be changed
+ * to just initialize_pcc(struct pceplib_infra_config *infra_config) */
+bool initialize_pcc(void);
+bool initialize_pcc_infra(struct pceplib_infra_config *infra_config);
+/* this function is blocking */
+bool initialize_pcc_wait_for_completion(void);
+bool destroy_pcc(void);
+
+
+/*
+ * PCEP session functions
+ */
+
+pcep_configuration *create_default_pcep_configuration(void);
+void destroy_pcep_configuration(pcep_configuration *config);
+
+/* Uses the standard PCEP TCP src and dest port = 4189.
+ * To use a specific dest or src port, set them other than 0 in the
+ * pcep_configuration. If src_ip is not set, INADDR_ANY will be used. */
+pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip);
+pcep_session *connect_pce_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip);
+void disconnect_pce(pcep_session *session);
+void send_message(pcep_session *session, struct pcep_message *msg,
+ bool free_after_send);
+
+void dump_pcep_session_counters(pcep_session *session);
+void reset_pcep_session_counters(pcep_session *session);
+
+/*
+ * Event Queue functions
+ */
+
+/* Returns true if the queue is empty, false otherwise */
+bool event_queue_is_empty(void);
+
+/* Return the number of events on the queue, 0 if empty */
+uint32_t event_queue_num_events_available(void);
+
+/* Return the next event on the queue, NULL if empty */
+struct pcep_event *event_queue_get_event(void);
+
+/* Free the PCEP Event resources, including the PCEP message */
+void destroy_pcep_event(struct pcep_event *event);
+
+const char *get_event_type_str(int event_type);
+
+
+#endif /* PCEPPCC_INCLUDE_PCEPPCCAPI_H_ */
diff --git a/pceplib/pcep_session_logic.c b/pceplib/pcep_session_logic.c
new file mode 100644
index 0000000000..5e4dae4900
--- /dev/null
+++ b/pceplib/pcep_session_logic.c
@@ -0,0 +1,683 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/*
+ * public API function implementations for the session_logic
+ */
+
+pcep_session_logic_handle *session_logic_handle_ = NULL;
+pcep_event_queue *session_logic_event_queue_ = NULL;
+int session_id_ = 0;
+
+void send_pcep_open(pcep_session *session); /* forward decl */
+
+static bool run_session_logic_common()
+{
+ if (session_logic_handle_ != NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Session Logic is already initialized.", __func__);
+ return false;
+ }
+
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+
+ session_logic_handle_->active = true;
+ session_logic_handle_->session_logic_condition = false;
+ session_logic_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ session_logic_handle_->session_event_queue = queue_initialize();
+
+ /* Initialize the event queue */
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+ if (pthread_mutex_init(&(session_logic_event_queue_->event_queue_mutex),
+ NULL)
+ != 0) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic event queue mutex.",
+ __func__);
+ return false;
+ }
+
+ pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
+ NULL);
+
+ if (pthread_mutex_init(&(session_logic_handle_->session_logic_mutex),
+ NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic mutex.",
+ __func__);
+ return false;
+ }
+
+ if (pthread_mutex_init(&(session_logic_handle_->session_list_mutex),
+ NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_list mutex.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool run_session_logic()
+{
+ if (!run_session_logic_common()) {
+ return false;
+ }
+
+ if (pthread_create(&(session_logic_handle_->session_logic_thread), NULL,
+ session_logic_loop, session_logic_handle_)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic thread.",
+ __func__);
+ return false;
+ }
+
+ if (!initialize_timers(session_logic_timer_expire_handler)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize session_logic timers.",
+ __func__);
+ return false;
+ }
+
+ /* No need to call initialize_socket_comm_loop() since it will be
+ * called internally when the first socket_comm_session is created. */
+
+ return true;
+}
+
+
+bool run_session_logic_with_infra(pceplib_infra_config *infra_config)
+{
+ if (infra_config == NULL) {
+ return run_session_logic();
+ }
+
+ /* Initialize the memory infrastructure before anything gets allocated
+ */
+ if (infra_config->pceplib_infra_mt != NULL
+ && infra_config->pceplib_messages_mt != NULL) {
+ pceplib_memory_initialize(
+ infra_config->pceplib_infra_mt,
+ infra_config->pceplib_messages_mt,
+ infra_config->malloc_func, infra_config->calloc_func,
+ infra_config->realloc_func, infra_config->strdup_func,
+ infra_config->free_func);
+ }
+
+ if (!run_session_logic_common()) {
+ return false;
+ }
+
+ /* Create the pcep_session_logic pthread so it can be managed externally
+ */
+ if (infra_config->pthread_create_func != NULL) {
+ if (infra_config->pthread_create_func(
+ &(session_logic_handle_->session_logic_thread),
+ NULL, session_logic_loop, session_logic_handle_,
+ "pcep_session_logic")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external session_logic thread.",
+ __func__);
+ return false;
+ }
+ } else {
+ if (pthread_create(
+ &(session_logic_handle_->session_logic_thread),
+ NULL, session_logic_loop, session_logic_handle_)) {
+ pcep_log(LOG_ERR,
+ "%s: Cannot initialize session_logic thread.",
+ __func__);
+ return false;
+ }
+ }
+
+ session_logic_event_queue_->event_callback =
+ infra_config->pcep_event_func;
+ session_logic_event_queue_->event_callback_data =
+ infra_config->external_infra_data;
+
+ if (!initialize_timers_external_infra(
+ session_logic_timer_expire_handler,
+ infra_config->external_infra_data,
+ infra_config->timer_create_func,
+ infra_config->timer_cancel_func,
+ infra_config->pthread_create_func)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic timers with infra.",
+ __func__);
+ return false;
+ }
+
+ /* We found a problem with the FRR sockets, where not all the KeepAlive
+ * messages were received, so if the pthread_create_func is set, the
+ * internal PCEPlib socket infrastructure will be used. */
+
+ /* For the SocketComm, the socket_read/write_func and the
+ * pthread_create_func are mutually exclusive. */
+ if (infra_config->pthread_create_func != NULL) {
+ if (!initialize_socket_comm_external_infra(
+ infra_config->external_infra_data, NULL, NULL,
+ infra_config->pthread_create_func)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic socket comm with infra.",
+ __func__);
+ return false;
+ }
+ } else if (infra_config->socket_read_func != NULL
+ && infra_config->socket_write_func != NULL) {
+ if (!initialize_socket_comm_external_infra(
+ infra_config->external_infra_data,
+ infra_config->socket_read_func,
+ infra_config->socket_write_func, NULL)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize session_logic socket comm with infra.",
+ __func__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool run_session_logic_wait_for_completion()
+{
+ if (!run_session_logic()) {
+ return false;
+ }
+
+ /* Blocking call, waits for session logic thread to complete */
+ pthread_join(session_logic_handle_->session_logic_thread, NULL);
+
+ return true;
+}
+
+
+bool stop_session_logic()
+{
+ if (session_logic_handle_ == NULL) {
+ pcep_log(LOG_WARNING, "%s: Session logic already stopped",
+ __func__);
+ return false;
+ }
+
+ session_logic_handle_->active = false;
+ teardown_timers();
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+ pthread_join(session_logic_handle_->session_logic_thread, NULL);
+
+ pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex));
+ ordered_list_destroy(session_logic_handle_->session_list);
+ queue_destroy(session_logic_handle_->session_event_queue);
+
+ /* destroy the event_queue */
+ pthread_mutex_destroy(&(session_logic_event_queue_->event_queue_mutex));
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+
+ /* Explicitly stop the socket comm loop started by the pcep_sessions */
+ destroy_socket_comm_loop();
+
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ session_logic_handle_ = NULL;
+
+ return true;
+}
+
+
+void close_pcep_session(pcep_session *session)
+{
+ close_pcep_session_with_reason(session, PCEP_CLOSE_REASON_NO);
+}
+
+void close_pcep_session_with_reason(pcep_session *session,
+ enum pcep_close_reason reason)
+{
+ struct pcep_message *close_msg = pcep_msg_create_close(reason);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id);
+
+ session_send_message(session, close_msg);
+ socket_comm_session_close_tcp_after_write(session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+}
+
+
+void destroy_pcep_session(pcep_session *session)
+{
+ if (session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot destroy NULL session",
+ __func__);
+ return;
+ }
+
+ /* Remove the session from the session_list and synchronize session
+ * destroy with the session_logic_loop, so that no in-flight events
+ * will be handled now that the session is destroyed. */
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ ordered_list_remove_first_node_equals(
+ session_logic_handle_->session_list, session);
+ pcep_log(LOG_DEBUG,
+ "%s: destroy_pcep_session delete session_list sessionPtr %p",
+ __func__, session);
+
+ pcep_session_cancel_timers(session);
+ delete_counters_group(session->pcep_session_counters);
+ queue_destroy_with_data(session->num_unknown_messages_time_queue);
+ socket_comm_session_teardown(session->socket_comm_session);
+
+ if (session->pcc_config.pcep_msg_versioning != NULL) {
+ pceplib_free(PCEPLIB_INFRA,
+ session->pcc_config.pcep_msg_versioning);
+ }
+
+ if (session->pce_config.pcep_msg_versioning != NULL) {
+ pceplib_free(PCEPLIB_INFRA,
+ session->pce_config.pcep_msg_versioning);
+ }
+
+ int session_id = session->session_id;
+ pceplib_free(PCEPLIB_INFRA, session);
+ pcep_log(LOG_INFO, "%s: [%ld-%ld] session [%d] destroyed", __func__,
+ time(NULL), pthread_self(), session_id);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+}
+
+void pcep_session_cancel_timers(pcep_session *session)
+{
+ if (session == NULL) {
+ return;
+ }
+
+ if (session->timer_id_dead_timer != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_dead_timer);
+ }
+
+ if (session->timer_id_keep_alive != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_keep_alive);
+ }
+
+ if (session->timer_id_open_keep_wait != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_open_keep_wait);
+ }
+
+ if (session->timer_id_open_keep_alive != TIMER_ID_NOT_SET) {
+ cancel_timer(session->timer_id_open_keep_alive);
+ }
+}
+
+/* Internal util function */
+static int get_next_session_id()
+{
+ if (session_id_ == INT_MAX) {
+ session_id_ = 0;
+ }
+
+ return session_id_++;
+}
+
+/* Internal util function */
+static pcep_session *create_pcep_session_pre_setup(pcep_configuration *config)
+{
+ if (config == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL config",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session));
+ memset(session, 0, sizeof(pcep_session));
+ session->session_id = get_next_session_id();
+ session->session_state = SESSION_STATE_INITIALIZED;
+ session->timer_id_open_keep_wait = TIMER_ID_NOT_SET;
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ session->stateful_pce = false;
+ session->num_unknown_messages_time_queue = queue_initialize();
+ session->pce_open_received = false;
+ session->pce_open_rejected = false;
+ session->pce_open_keep_alive_sent = false;
+ session->pcc_open_rejected = false;
+ session->pce_open_accepted = false;
+ session->pcc_open_accepted = false;
+ session->destroy_session_after_write = false;
+ session->lsp_db_version = config->lsp_db_version;
+ memcpy(&(session->pcc_config), config, sizeof(pcep_configuration));
+ /* copy the pcc_config to the pce_config until we receive the open
+ * keep_alive response */
+ memcpy(&(session->pce_config), config, sizeof(pcep_configuration));
+ if (config->pcep_msg_versioning != NULL) {
+ session->pcc_config.pcep_msg_versioning = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memcpy(session->pcc_config.pcep_msg_versioning,
+ config->pcep_msg_versioning,
+ sizeof(struct pcep_versioning));
+ session->pce_config.pcep_msg_versioning = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct pcep_versioning));
+ memcpy(session->pce_config.pcep_msg_versioning,
+ config->pcep_msg_versioning,
+ sizeof(struct pcep_versioning));
+ }
+
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ ordered_list_add_node(session_logic_handle_->session_list, session);
+ pcep_log(
+ LOG_DEBUG,
+ "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
+ __func__, session);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+
+ return session;
+}
+
+/* Internal util function */
+static bool create_pcep_session_post_setup(pcep_session *session)
+{
+ if (!socket_comm_session_connect_tcp(session->socket_comm_session)) {
+ pcep_log(LOG_WARNING, "%s: Cannot establish TCP socket.",
+ __func__);
+ destroy_pcep_session(session);
+
+ return false;
+ }
+
+ session->time_connected = time(NULL);
+ create_session_counters(session);
+
+ send_pcep_open(session);
+
+ session->session_state = SESSION_STATE_PCEP_CONNECTING;
+ session->timer_id_open_keep_wait =
+ create_timer(session->pcc_config.keep_alive_seconds, session);
+ // session->session_state = SESSION_STATE_OPENED;
+
+ return true;
+}
+
+pcep_session *create_pcep_session(pcep_configuration *config,
+ struct in_addr *pce_ip)
+{
+ if (pce_ip == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL pce_ip",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session = create_pcep_session_pre_setup(config);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ session->socket_comm_session = socket_comm_session_initialize_with_src(
+ NULL, session_logic_msg_ready_handler,
+ session_logic_message_sent_handler,
+ session_logic_conn_except_notifier, &(config->src_ip.src_ipv4),
+ ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->src_pcep_port),
+ pce_ip,
+ ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->dst_pcep_port),
+ config->socket_connect_timeout_millis,
+ config->tcp_authentication_str, config->is_tcp_auth_md5,
+ session);
+ if (session->socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot establish socket_comm_session.", __func__);
+ destroy_pcep_session(session);
+
+ return NULL;
+ }
+
+ if (create_pcep_session_post_setup(session) == false) {
+ return NULL;
+ }
+
+ return session;
+}
+
+pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip)
+{
+ if (pce_ip == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create pcep session with NULL pce_ip",
+ __func__);
+ return NULL;
+ }
+
+ pcep_session *session = create_pcep_session_pre_setup(config);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ session->socket_comm_session =
+ socket_comm_session_initialize_with_src_ipv6(
+ NULL, session_logic_msg_ready_handler,
+ session_logic_message_sent_handler,
+ session_logic_conn_except_notifier,
+ &(config->src_ip.src_ipv6),
+ ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->src_pcep_port),
+ pce_ip,
+ ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
+ : config->dst_pcep_port),
+ config->socket_connect_timeout_millis,
+ config->tcp_authentication_str, config->is_tcp_auth_md5,
+ session);
+ if (session->socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot establish ipv6 socket_comm_session.",
+ __func__);
+ destroy_pcep_session(session);
+
+ return NULL;
+ }
+
+ if (create_pcep_session_post_setup(session) == false) {
+ return NULL;
+ }
+
+ return session;
+}
+
+
+void session_send_message(pcep_session *session, struct pcep_message *message)
+{
+ pcep_encode_message(message, session->pcc_config.pcep_msg_versioning);
+ socket_comm_session_send_message(session->socket_comm_session,
+ (char *)message->encoded_message,
+ message->encoded_message_length, true);
+
+ increment_message_tx_counters(session, message);
+
+ /* The message->encoded_message will be freed in
+ * socket_comm_session_send_message() once sent.
+ * Setting to NULL here so pcep_msg_free_message() does not free it */
+ message->encoded_message = NULL;
+ pcep_msg_free_message(message);
+}
+
+
+/* This function is also used in pcep_session_logic_states.c */
+struct pcep_message *create_pcep_open(pcep_session *session)
+{
+ /* create and send PCEP open
+ * with PCEP, the PCC sends the config the PCE should use in the open
+ * message,
+ * and the PCE will send an open with the config the PCC should use. */
+ double_linked_list *tlv_list = dll_initialize();
+ if (session->pcc_config.support_stateful_pce_lsp_update
+ || session->pcc_config.support_pce_lsp_instantiation
+ || session->pcc_config.support_include_db_version
+ || session->pcc_config.support_lsp_triggered_resync
+ || session->pcc_config.support_lsp_delta_sync
+ || session->pcc_config.support_pce_triggered_initial_sync) {
+ /* Prepend this TLV as the first in the list */
+ dll_append(
+ tlv_list,
+ pcep_tlv_create_stateful_pce_capability(
+ session->pcc_config
+ .support_stateful_pce_lsp_update, /* U
+ flag
+ */
+ session->pcc_config
+ .support_include_db_version, /* S flag
+ */
+ session->pcc_config
+ .support_lsp_triggered_resync, /* T flag
+ */
+ session->pcc_config
+ .support_lsp_delta_sync, /* D flag */
+ session->pcc_config
+ .support_pce_triggered_initial_sync, /* F flag */
+ session->pcc_config
+ .support_pce_lsp_instantiation)); /* I
+ flag
+ */
+ }
+
+ if (session->pcc_config.support_include_db_version) {
+ if (session->pcc_config.lsp_db_version != 0) {
+ dll_append(tlv_list,
+ pcep_tlv_create_lsp_db_version(
+ session->pcc_config.lsp_db_version));
+ }
+ }
+
+ if (session->pcc_config.support_sr_te_pst) {
+ bool flag_n = false;
+ bool flag_x = false;
+ if (session->pcc_config.pcep_msg_versioning
+ ->draft_ietf_pce_segment_routing_07
+ == false) {
+ flag_n = session->pcc_config.pcc_can_resolve_nai_to_sid;
+ flag_x = (session->pcc_config.max_sid_depth == 0);
+ }
+
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
+ pcep_tlv_create_sr_pce_capability(
+ flag_n, flag_x,
+ session->pcc_config.max_sid_depth);
+
+ double_linked_list *sub_tlv_list = NULL;
+ if (session->pcc_config.pcep_msg_versioning
+ ->draft_ietf_pce_segment_routing_07
+ == true) {
+ /* With draft07, send the sr_pce_cap_tlv as a normal TLV
+ */
+ dll_append(tlv_list, sr_pce_cap_tlv);
+ } else {
+ /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in
+ * the path_setup_type_capability TLV */
+ sub_tlv_list = dll_initialize();
+ dll_append(sub_tlv_list, sr_pce_cap_tlv);
+ }
+
+ uint8_t *pst =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
+ *pst = SR_TE_PST;
+ double_linked_list *pst_list = dll_initialize();
+ dll_append(pst_list, pst);
+ dll_append(tlv_list, pcep_tlv_create_path_setup_type_capability(
+ pst_list, sub_tlv_list));
+ }
+
+ struct pcep_message *open_msg = pcep_msg_create_open_with_tlvs(
+ session->pcc_config.keep_alive_seconds,
+ session->pcc_config.dead_timer_seconds, session->session_id,
+ tlv_list);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), tlv_list->num_entries,
+ session->session_id);
+
+ return (open_msg);
+}
+
+
+void send_pcep_open(pcep_session *session)
+{
+ session_send_message(session, create_pcep_open(session));
+}
+
+/* This is a blocking call, since it is synchronized with destroy_pcep_session()
+ * and session_logic_loop(). It may be possible that the session has been
+ * deleted but API users havent been informed yet.
+ */
+bool session_exists(pcep_session *session)
+{
+ if (session_logic_handle_ == NULL) {
+ pcep_log(LOG_DEBUG,
+ "%s: session_exists session_logic_handle_ is NULL",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
+ bool retval =
+ (ordered_list_find(session_logic_handle_->session_list, session)
+ != NULL);
+ pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
+
+ return retval;
+}
diff --git a/pceplib/pcep_session_logic.h b/pceplib/pcep_session_logic.h
new file mode 100644
index 0000000000..a082ec66da
--- /dev/null
+++ b/pceplib/pcep_session_logic.h
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPSESSIONLOGIC_H_
+#define INCLUDE_PCEPSESSIONLOGIC_H_
+
+#include <stdbool.h>
+#include <netinet/tcp.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_socket_comm.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_timers.h"
+#include "pcep_utils_queue.h"
+#include "pcep_utils_memory.h"
+
+#define PCEP_TCP_PORT 4189
+
+typedef struct pcep_configuration_ {
+ /* These are the configuration values that will
+ * be sent to the PCE in the PCEP Open message */
+ int keep_alive_seconds;
+ int dead_timer_seconds;
+ int dead_timer_pce_negotiated_seconds; /* Config data negotiated with
+ PCE */
+ int keep_alive_pce_negotiated_timer_seconds; /* Config data negotiated
+ with PCE */
+ int request_time_seconds;
+
+ /* These are the acceptable ranges of values received by
+ * the PCE in the initial PCEP Open Message. If a value is
+ * received outside of these ranges, then the Open message
+ * will be rejected. */
+ int min_keep_alive_seconds;
+ int max_keep_alive_seconds;
+ int min_dead_timer_seconds;
+ int max_dead_timer_seconds;
+
+ /* If more than this many unknown messages/requests are received
+ * per minute, then the session will be closed. */
+ int max_unknown_messages;
+ int max_unknown_requests;
+
+ /* Maximum amount of time to wait to connect to the
+ * PCE TCP socket before failing, in milliseconds. */
+ uint32_t socket_connect_timeout_millis;
+
+ /* Set if the PCE/PCC will support stateful PCE LSP Updates
+ * according to RCF8231, section 7.1.1, defaults to true.
+ * Will cause an additional TLV to be sent from the PCC in
+ * the PCEP Open */
+ bool support_stateful_pce_lsp_update;
+
+ /* RFC 8281: I-bit, the PCC allows instantiation of an LSP by a PCE */
+ bool support_pce_lsp_instantiation;
+
+ /* RFC 8232: S-bit, the PCC will include the LSP-DB-VERSION
+ * TLV in each LSP object */
+ bool support_include_db_version;
+
+ /* Only set if support_include_db_version is true and if the LSP-DB
+ * survived a restart and is available. If this has a value other than
+ * 0, then a LSP-DB-VERSION TLV will be sent in the OPEN object. This
+ * value will be copied over to the pcep_session upon init. */
+ uint64_t lsp_db_version;
+
+ /* RFC 8232: T-bit, the PCE can trigger resynchronization of
+ * LSPs at any point in the life of the session */
+ bool support_lsp_triggered_resync;
+
+ /* RFC 8232: D-bit, the PCEP speaker allows incremental (delta)
+ * State Synchronization */
+ bool support_lsp_delta_sync;
+
+ /* RFC 8232: F-bit, the PCE SHOULD trigger initial (first)
+ * State Synchronization */
+ bool support_pce_triggered_initial_sync;
+
+ /* draft-ietf-pce-segment-routing-16: Send a SR PCE Capability
+ * sub-TLV in a Path Setup Type Capability TLV with a PST = 1,
+ * Path is setup using SR TE. */
+ bool support_sr_te_pst;
+ /* Used in the SR PCE Capability sub-TLV */
+ bool pcc_can_resolve_nai_to_sid;
+ /* Used in the SR TE Capability sub-TLV, 0 means there are no max sid
+ * limits */
+ uint8_t max_sid_depth;
+
+ /* If set to 0, then the default 4189 PCEP port will be used */
+ uint16_t dst_pcep_port;
+
+ /* If set to 0, then the default 4189 PCEP port will be used.
+ * This is according to the RFC5440, Section 5 */
+ uint16_t src_pcep_port;
+
+ union src_ip {
+ struct in_addr src_ipv4;
+ struct in6_addr src_ipv6;
+ } src_ip;
+ bool is_src_ipv6;
+
+ struct pcep_versioning *pcep_msg_versioning;
+
+ char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN];
+ bool is_tcp_auth_md5; /* true: RFC 2385, false: RFC 5925 */
+
+} pcep_configuration;
+
+
+typedef enum pcep_session_state_ {
+ SESSION_STATE_UNKNOWN = 0,
+ SESSION_STATE_INITIALIZED = 1,
+ SESSION_STATE_PCEP_CONNECTING = 2,
+ SESSION_STATE_PCEP_CONNECTED = 3
+
+} pcep_session_state;
+
+
+typedef struct pcep_session_ {
+ int session_id;
+ pcep_session_state session_state;
+ int timer_id_open_keep_wait;
+ int timer_id_open_keep_alive;
+ int timer_id_dead_timer;
+ int timer_id_keep_alive;
+ bool pce_open_received;
+ bool pce_open_rejected;
+ bool pce_open_accepted;
+ bool pce_open_keep_alive_sent;
+ bool pcc_open_rejected;
+ bool pcc_open_accepted;
+ bool stateful_pce;
+ time_t time_connected;
+ uint64_t lsp_db_version;
+ queue_handle *num_unknown_messages_time_queue;
+ /* set this flag when finalizing the session */
+ bool destroy_session_after_write;
+ pcep_socket_comm_session *socket_comm_session;
+ /* Configuration sent from the PCC to the PCE */
+ pcep_configuration pcc_config;
+ /* Configuration received from the PCE, to be used in the PCC */
+ pcep_configuration pce_config;
+ struct counters_group *pcep_session_counters;
+
+} pcep_session;
+
+
+typedef enum pcep_event_type {
+ MESSAGE_RECEIVED = 0,
+ PCE_CLOSED_SOCKET = 1,
+ PCE_SENT_PCEP_CLOSE = 2,
+ PCE_DEAD_TIMER_EXPIRED = 3,
+ PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED = 4,
+ PCC_CONNECTED_TO_PCE = 100,
+ PCC_CONNECTION_FAILURE = 101,
+ PCC_PCEP_SESSION_CLOSED = 102,
+ PCC_RCVD_INVALID_OPEN = 103,
+ PCC_SENT_INVALID_OPEN = 104,
+ PCC_RCVD_MAX_INVALID_MSGS = 105,
+ PCC_RCVD_MAX_UNKOWN_MSGS = 106
+
+} pcep_event_type;
+
+
+typedef struct pcep_event {
+ enum pcep_event_type event_type;
+ time_t event_time;
+ struct pcep_message *message;
+ pcep_session *session;
+
+} pcep_event;
+
+typedef void (*pceplib_pcep_event_callback)(void *cb_data, pcep_event *);
+typedef int (*pthread_create_callback)(pthread_t *pthread_id,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *),
+ void *data, const char *thread_name);
+
+
+typedef struct pcep_event_queue {
+ /* The event_queue and event_callback are mutually exclusive.
+ * If the event_callback is configured, then the event_queue
+ * will not be used. */
+ queue_handle *event_queue;
+ pthread_mutex_t event_queue_mutex;
+ pceplib_pcep_event_callback event_callback;
+ void *event_callback_data;
+
+} pcep_event_queue;
+
+
+typedef struct pceplib_infra_config {
+ /* Memory infrastructure */
+ void *pceplib_infra_mt;
+ void *pceplib_messages_mt;
+ pceplib_malloc_func malloc_func;
+ pceplib_calloc_func calloc_func;
+ pceplib_realloc_func realloc_func;
+ pceplib_strdup_func strdup_func;
+ pceplib_free_func free_func;
+
+ /* External Timer and Socket infrastructure */
+ void *external_infra_data;
+ ext_timer_create timer_create_func;
+ ext_timer_cancel timer_cancel_func;
+ ext_socket_write socket_write_func;
+ ext_socket_read socket_read_func;
+
+ /* External pcep_event infrastructure */
+ pceplib_pcep_event_callback pcep_event_func;
+
+ /* Callback to create pthreads */
+ pthread_create_callback pthread_create_func;
+
+} pceplib_infra_config;
+
+/*
+ * Counters Sub-groups definitions
+ */
+typedef enum pcep_session_counters_subgroup_ids {
+ COUNTER_SUBGROUP_ID_RX_MSG = 0,
+ COUNTER_SUBGROUP_ID_TX_MSG = 1,
+ COUNTER_SUBGROUP_ID_RX_OBJ = 2,
+ COUNTER_SUBGROUP_ID_TX_OBJ = 3,
+ COUNTER_SUBGROUP_ID_RX_SUBOBJ = 4,
+ COUNTER_SUBGROUP_ID_TX_SUBOBJ = 5,
+ COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ = 6,
+ COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ = 7,
+ COUNTER_SUBGROUP_ID_RX_TLV = 8,
+ COUNTER_SUBGROUP_ID_TX_TLV = 9,
+ COUNTER_SUBGROUP_ID_EVENT = 10
+
+} pcep_session_counters_subgroup_ids;
+
+bool run_session_logic(void);
+bool run_session_logic_with_infra(pceplib_infra_config *infra_config);
+
+bool run_session_logic_wait_for_completion(void);
+
+bool stop_session_logic(void);
+
+/* Uses the standard PCEP TCP dest port = 4189 and an ephemeral src port.
+ * To use a specific dest or src port, set them other than 0 in the
+ * pcep_configuration. */
+pcep_session *create_pcep_session(pcep_configuration *config,
+ struct in_addr *pce_ip);
+pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
+ struct in6_addr *pce_ip);
+
+/* Send a PCEP close for this pcep_session */
+void close_pcep_session(pcep_session *session);
+void close_pcep_session_with_reason(pcep_session *session,
+ enum pcep_close_reason);
+
+/* Destroy the PCEP session, a PCEP close should have
+ * already been sent with close_pcep_session() */
+void destroy_pcep_session(pcep_session *session);
+
+void pcep_session_cancel_timers(pcep_session *session);
+
+/* Increments transmitted message counters, additionally counters for the
+ * objects, sub-objects, and TLVs in the message will be incremented. Received
+ * counters are incremented internally. */
+void increment_message_tx_counters(pcep_session *session,
+ struct pcep_message *message);
+
+bool session_exists(pcep_session *session);
+
+#endif /* INCLUDE_PCEPSESSIONLOGIC_H_ */
diff --git a/pceplib/pcep_session_logic_counters.c b/pceplib/pcep_session_logic_counters.c
new file mode 100644
index 0000000000..a6bd41b4f1
--- /dev/null
+++ b/pceplib/pcep_session_logic_counters.c
@@ -0,0 +1,450 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * PCEP session logic counters configuration.
+ */
+
+#include <stdio.h>
+#include <time.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+
+void increment_message_counters(pcep_session *session,
+ struct pcep_message *message, bool is_rx);
+
+void create_session_counters(pcep_session *session)
+{
+ /*
+ * Message RX and TX counters
+ */
+ struct counters_subgroup *rx_msg_subgroup = create_counters_subgroup(
+ "RX Message counters", COUNTER_SUBGROUP_ID_RX_MSG,
+ PCEP_TYPE_MAX + 1);
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_OPEN,
+ "Message Open");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_KEEPALIVE,
+ "Message KeepAlive");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREQ,
+ "Message PcReq");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREP,
+ "Message PcRep");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCNOTF,
+ "Message Notify");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_ERROR,
+ "Message Error");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_CLOSE,
+ "Message Close");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_REPORT,
+ "Message Report");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_UPDATE,
+ "Message Update");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_INITIATE,
+ "Message Initiate");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_START_TLS,
+ "Message StartTls");
+ create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_MAX,
+ "Message Erroneous");
+
+ struct counters_subgroup *tx_msg_subgroup =
+ clone_counters_subgroup(rx_msg_subgroup, "TX Message counters",
+ COUNTER_SUBGROUP_ID_TX_MSG);
+
+ /*
+ * Object RX and TX counters
+ */
+
+ /* For the Endpoints, the ID will be either 64 or 65, so setting
+ * num_counters to 100 */
+ struct counters_subgroup *rx_obj_subgroup = create_counters_subgroup(
+ "RX Object counters", COUNTER_SUBGROUP_ID_RX_OBJ, 100);
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_OPEN,
+ "Object Open");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RP,
+ "Object RP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOPATH,
+ "Object Nopath");
+ create_subgroup_counter(
+ rx_obj_subgroup,
+ ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV4),
+ "Object Endpoint IPv4");
+ create_subgroup_counter(
+ rx_obj_subgroup,
+ ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV6),
+ "Object Endpoint IPv6");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_BANDWIDTH,
+ "Object Bandwidth");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_METRIC,
+ "Object Metric");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERO,
+ "Object ERO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RRO,
+ "Object RRO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSPA,
+ "Object LSPA");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_IRO,
+ "Object IRO");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SVEC,
+ "Object SVEC");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOTF,
+ "Object Notify");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERROR,
+ "Object Error");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_CLOSE,
+ "Object Close");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSP,
+ "Object LSP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SRP,
+ "Object SRP");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_VENDOR_INFO,
+ "Object Vendor Info");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_INTER_LAYER,
+ "Object Inter-Layer");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SWITCH_LAYER,
+ "Object Switch-Layer");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_REQ_ADAP_CAP,
+ "Object Requested Adap-Cap");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SERVER_IND,
+ "Object Server-Indication");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ASSOCIATION,
+ "Object Association");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX,
+ "Object Unknown");
+ create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX + 1,
+ "Object Erroneous");
+
+ struct counters_subgroup *tx_obj_subgroup =
+ clone_counters_subgroup(rx_obj_subgroup, "TX Object counters",
+ COUNTER_SUBGROUP_ID_TX_OBJ);
+
+ /*
+ * Sub-Object RX and TX counters
+ */
+ struct counters_subgroup *rx_subobj_subgroup = create_counters_subgroup(
+ "RX RO Sub-Object counters", COUNTER_SUBGROUP_ID_RX_SUBOBJ,
+ RO_SUBOBJ_UNKNOWN + 2);
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV4,
+ "RO Sub-Object IPv4");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV6,
+ "RO Sub-Object IPv6");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_LABEL,
+ "RO Sub-Object Label");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_UNNUM,
+ "RO Sub-Object Unnum");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_ASN,
+ "RO Sub-Object ASN");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_SR,
+ "RO Sub-Object SR");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN,
+ "RO Sub-Object Unknown");
+ create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN + 1,
+ "RO Sub-Object Erroneous");
+
+ struct counters_subgroup *tx_subobj_subgroup = clone_counters_subgroup(
+ rx_subobj_subgroup, "TX RO Sub-Object counters",
+ COUNTER_SUBGROUP_ID_TX_SUBOBJ);
+
+ /*
+ * RO SR Sub-Object RX and TX counters
+ */
+ struct counters_subgroup *rx_subobj_sr_nai_subgroup =
+ create_counters_subgroup("RX RO SR NAI Sub-Object counters",
+ COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN + 1);
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_ABSENT,
+ "RO Sub-Object SR NAI absent");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE,
+ "RO Sub-Object SR NAI IPv4 Node");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE,
+ "RO Sub-Object SR NAI IPv6 Node");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY,
+ "RO Sub-Object SR NAI IPv4 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY,
+ "RO Sub-Object SR NAI IPv6 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY,
+ "RO Sub-Object SR NAI Unnumbered IPv4 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY,
+ "RO Sub-Object SR NAI Link Local IPv6 Adj");
+ create_subgroup_counter(rx_subobj_sr_nai_subgroup,
+ PCEP_SR_SUBOBJ_NAI_UNKNOWN,
+ "RO Sub-Object SR NAI Unknown");
+
+ struct counters_subgroup *tx_subobj_sr_nai_subgroup =
+ clone_counters_subgroup(rx_subobj_sr_nai_subgroup,
+ "TX RO SR NAI Sub-Object counters",
+ COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ);
+
+ /*
+ * TLV RX and TX counters
+ */
+ struct counters_subgroup *rx_tlv_subgroup = create_counters_subgroup(
+ "RX TLV counters", COUNTER_SUBGROUP_ID_RX_TLV,
+ PCEP_OBJ_TLV_TYPE_UNKNOWN + 1);
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR,
+ "TLV No Path Vector");
+ create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_VENDOR_INFO,
+ "TLV Vendor Info");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY,
+ "TLV Stateful PCE Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME,
+ "TLV Symbolic Path Name");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS,
+ "TLV IPv4 LSP Identifier");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS,
+ "TLV IPv6 LSP Identifier");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE,
+ "TLV LSP Error Code");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC,
+ "TLV RSVP Error Spec");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION,
+ "TLV LSP DB Version");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID,
+ "TLV Speaker Entity ID");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY,
+ "TLV SR PCE Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE,
+ "TLV Path Setup Type");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY,
+ "TLV Path Setup Type Capability");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID,
+ "TLV SR Policy PolId");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME,
+ "TLV SR Policy PolName");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID,
+ "TLV SR Policy CpathId");
+ create_subgroup_counter(rx_tlv_subgroup,
+ PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE,
+ "TLV SR Policy CpathRef");
+ create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_UNKNOWN,
+ "TLV Unknown");
+
+ struct counters_subgroup *tx_tlv_subgroup = clone_counters_subgroup(
+ rx_tlv_subgroup, "TX TLV counters", COUNTER_SUBGROUP_ID_TX_TLV);
+
+ /*
+ * PCEP Event counters
+ */
+ struct counters_subgroup *events_subgroup = create_counters_subgroup(
+ "Events counters", COUNTER_SUBGROUP_ID_EVENT, MAX_COUNTERS);
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT,
+ "PCC connect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT,
+ "PCE connect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT,
+ "PCC disconnect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT,
+ "PCE disconnect");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE,
+ "Timer KeepAlive expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER,
+ "Timer DeadTimer expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT,
+ "Timer OpenKeepWait expired");
+ create_subgroup_counter(events_subgroup,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE,
+ "Timer OpenKeepAlive expired");
+
+ /*
+ * Create the parent counters group
+ */
+ time_t now = time(NULL);
+ char counters_name[MAX_COUNTER_STR_LENGTH] = {0};
+ char ip_str[40] = {0};
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ ip_str, 40);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ ip_str, 40);
+ }
+ snprintf(counters_name, MAX_COUNTER_STR_LENGTH,
+ "PCEP Session [%d], connected to [%s] for [%u seconds]",
+ session->session_id, ip_str,
+ (uint32_t)(now - session->time_connected));
+ /* The (time(NULL) - session->time_connected) will probably be 0,
+ * so the group name will be updated when the counters are dumped */
+ session->pcep_session_counters =
+ create_counters_group(counters_name, MAX_COUNTER_GROUPS);
+
+ /*
+ * Add all the subgroups to the parent counters group
+ */
+ add_counters_subgroup(session->pcep_session_counters, rx_msg_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_msg_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, rx_obj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_obj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ rx_subobj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ tx_subobj_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ rx_subobj_sr_nai_subgroup);
+ add_counters_subgroup(session->pcep_session_counters,
+ tx_subobj_sr_nai_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, rx_tlv_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, tx_tlv_subgroup);
+ add_counters_subgroup(session->pcep_session_counters, events_subgroup);
+}
+
+/* Internal util function used by increment_message_rx_counters or
+ * increment_message_tx_counters */
+void increment_message_counters(pcep_session *session,
+ struct pcep_message *message, bool is_rx)
+{
+ uint16_t counter_subgroup_id_msg = (is_rx ? COUNTER_SUBGROUP_ID_RX_MSG
+ : COUNTER_SUBGROUP_ID_TX_MSG);
+ uint16_t counter_subgroup_id_obj = (is_rx ? COUNTER_SUBGROUP_ID_RX_OBJ
+ : COUNTER_SUBGROUP_ID_TX_OBJ);
+ uint16_t counter_subgroup_id_subobj =
+ (is_rx ? COUNTER_SUBGROUP_ID_RX_SUBOBJ
+ : COUNTER_SUBGROUP_ID_TX_SUBOBJ);
+ uint16_t counter_subgroup_id_ro_sr_subobj =
+ (is_rx ? COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ
+ : COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ);
+ uint16_t counter_subgroup_id_tlv = (is_rx ? COUNTER_SUBGROUP_ID_RX_TLV
+ : COUNTER_SUBGROUP_ID_TX_TLV);
+
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_msg, message->msg_header->type);
+
+ /* Iterate the objects */
+ double_linked_list_node *obj_node =
+ (message->obj_list == NULL ? NULL : message->obj_list->head);
+ for (; obj_node != NULL; obj_node = obj_node->next_node) {
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)obj_node->data;
+
+ /* Handle class: PCEP_OBJ_CLASS_ENDPOINTS,
+ * type: PCEP_OBJ_TYPE_ENDPOINT_IPV4 or
+ * PCEP_OBJ_TYPE_ENDPOINT_IPV6 */
+ uint16_t obj_counter_id =
+ (obj->object_class == PCEP_OBJ_CLASS_ENDPOINTS
+ ? (obj->object_class << 4) | obj->object_type
+ : obj->object_class);
+
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_obj, obj_counter_id);
+
+ /* Iterate the RO Sub-objects */
+ if (obj->object_class == PCEP_OBJ_CLASS_ERO
+ || obj->object_class == PCEP_OBJ_CLASS_IRO
+ || obj->object_class == PCEP_OBJ_CLASS_RRO) {
+ struct pcep_object_ro *ro_obj =
+ (struct pcep_object_ro *)obj;
+
+ double_linked_list_node *ro_subobj_node =
+ (ro_obj->sub_objects == NULL
+ ? NULL
+ : ro_obj->sub_objects->head);
+ for (; ro_subobj_node != NULL;
+ ro_subobj_node = ro_subobj_node->next_node) {
+ struct pcep_object_ro_subobj *ro_subobj =
+ (struct pcep_object_ro_subobj *)
+ ro_subobj_node->data;
+ increment_counter(
+ session->pcep_session_counters,
+ counter_subgroup_id_subobj,
+ ro_subobj->ro_subobj_type);
+
+ /* Handle the ro subobj type RO_SUBOBJ_TYPE_SR
+ * different NAI types */
+ if (ro_subobj->ro_subobj_type
+ == RO_SUBOBJ_TYPE_SR) {
+ struct pcep_ro_subobj_sr *ro_sr_subobj =
+ (struct pcep_ro_subobj_sr *)
+ ro_subobj;
+ increment_counter(
+ session->pcep_session_counters,
+ counter_subgroup_id_ro_sr_subobj,
+ ro_sr_subobj->nai_type);
+ }
+ }
+ }
+
+ /* Iterate the TLVs */
+ double_linked_list_node *tlv_node =
+ (obj->tlv_list == NULL ? NULL : obj->tlv_list->head);
+ for (; tlv_node != NULL; tlv_node = tlv_node->next_node) {
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)tlv_node->data;
+ increment_counter(session->pcep_session_counters,
+ counter_subgroup_id_tlv, tlv->type);
+ }
+ }
+}
+
+void increment_message_rx_counters(pcep_session *session,
+ struct pcep_message *message)
+{
+ increment_message_counters(session, message, true);
+}
+
+void increment_message_tx_counters(pcep_session *session,
+ struct pcep_message *message)
+{
+ increment_message_counters(session, message, false);
+}
+
+void increment_event_counters(
+ pcep_session *session,
+ pcep_session_counters_event_counter_ids counter_id)
+{
+ increment_counter(session->pcep_session_counters,
+ COUNTER_SUBGROUP_ID_EVENT, counter_id);
+}
diff --git a/pceplib/pcep_session_logic_internals.h b/pceplib/pcep_session_logic_internals.h
new file mode 100644
index 0000000000..004459b918
--- /dev/null
+++ b/pceplib/pcep_session_logic_internals.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Internal Session Logic declarations, not intended to be in the public API.
+ */
+
+#ifndef SRC_PCEPSESSIONLOGICINTERNALS_H_
+#define SRC_PCEPSESSIONLOGICINTERNALS_H_
+
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_msg_tools.h"
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_queue.h"
+
+
+typedef struct pcep_session_logic_handle_ {
+ pthread_t session_logic_thread;
+ pthread_mutex_t session_logic_mutex;
+ pthread_cond_t session_logic_cond_var;
+ bool session_logic_condition;
+ bool active;
+
+ ordered_list_handle *session_list;
+ pthread_mutex_t session_list_mutex;
+ /* Internal timers and socket events */
+ queue_handle *session_event_queue;
+
+} pcep_session_logic_handle;
+
+
+/* Used internally for Session events: message received, timer expired,
+ * or socket closed */
+typedef struct pcep_session_event_ {
+ pcep_session *session;
+ int expired_timer_id;
+ double_linked_list *received_msg_list;
+ bool socket_closed;
+
+} pcep_session_event;
+
+/* Event Counters counter-id definitions */
+typedef enum pcep_session_counters_event_counter_ids {
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT = 0,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT = 1,
+ PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT = 2,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT = 3,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE = 4,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER = 5,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT = 6,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE = 7
+
+} pcep_session_counters_event_counter_ids;
+
+/* functions implemented in pcep_session_logic_loop.c */
+void *session_logic_loop(void *data);
+int session_logic_msg_ready_handler(void *data, int socket_fd);
+void session_logic_message_sent_handler(void *data, int socket_fd);
+void session_logic_conn_except_notifier(void *data, int socket_fd);
+void session_logic_timer_expire_handler(void *data, int timer_id);
+
+void handle_timer_event(pcep_session_event *event);
+void handle_socket_comm_event(pcep_session_event *event);
+void session_send_message(pcep_session *session, struct pcep_message *message);
+
+/* defined in pcep_session_logic_states.c */
+void send_pcep_error(pcep_session *session, enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+void enqueue_event(pcep_session *session, pcep_event_type event_type,
+ struct pcep_message *message);
+void increment_unknown_message(pcep_session *session);
+
+/* defined in pcep_session_logic_counters.c */
+void create_session_counters(pcep_session *session);
+void increment_event_counters(
+ pcep_session *session,
+ pcep_session_counters_event_counter_ids counter_id);
+void increment_message_rx_counters(pcep_session *session,
+ struct pcep_message *message);
+
+/* defined in pcep_session_logic.c, also used in pcep_session_logic_states.c */
+struct pcep_message *create_pcep_open(pcep_session *session);
+
+#endif /* SRC_PCEPSESSIONLOGICINTERNALS_H_ */
diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c
new file mode 100644
index 0000000000..5705ff2000
--- /dev/null
+++ b/pceplib/pcep_session_logic_loop.c
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* global var needed for callback handlers */
+extern pcep_session_logic_handle *session_logic_handle_;
+
+/* internal util function to create session_event's */
+static pcep_session_event *create_session_event(pcep_session *session)
+{
+ pcep_session_event *event =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session_event));
+ event->session = session;
+ event->expired_timer_id = TIMER_ID_NOT_SET;
+ event->received_msg_list = NULL;
+ event->socket_closed = false;
+
+ return event;
+}
+
+
+/* A function pointer to this function is passed to pcep_socket_comm
+ * for each pcep_session creation, so it will be called whenever
+ * messages are ready to be read. This function will be called
+ * by the socket_comm thread.
+ * This function will decode the read PCEP message and give it
+ * to the session_logic_loop so it can be handled by the session_logic
+ * state machine. */
+int session_logic_msg_ready_handler(void *data, int socket_fd)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle msg_ready with NULL data",
+ __func__);
+ return -1;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a message ready notification while the session logic is not active",
+ __func__);
+ return -1;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+
+ /* This event will ultimately be handled by handle_socket_comm_event()
+ * in pcep_session_logic_states.c */
+ pcep_session_event *rcvd_msg_event = create_session_event(session);
+
+ int msg_length = 0;
+ double_linked_list *msg_list = pcep_msg_read(socket_fd);
+
+ if (msg_list == NULL) {
+ /* The socket was closed, or there was a socket read error */
+ pcep_log(LOG_INFO,
+ "%s: PCEP connection closed for session [%d]",
+ __func__, session->session_id);
+ dll_destroy(msg_list);
+ rcvd_msg_event->socket_closed = true;
+ socket_comm_session_teardown(session->socket_comm_session);
+ pcep_session_cancel_timers(session);
+ session->socket_comm_session = NULL;
+ session->session_state = SESSION_STATE_INITIALIZED;
+ enqueue_event(session, PCE_CLOSED_SOCKET, NULL);
+ } else if (msg_list->num_entries == 0) {
+ /* Invalid message received */
+ increment_unknown_message(session);
+ } else {
+ /* Just logging the first of potentially several messages
+ * received */
+ struct pcep_message *msg =
+ ((struct pcep_message *)msg_list->head->data);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
+ __func__, time(NULL), pthread_self(),
+ msg->msg_header->type, msg->encoded_message_length,
+ session->session_id);
+
+ rcvd_msg_event->received_msg_list = msg_list;
+ msg_length = msg->encoded_message_length;
+ }
+
+ queue_enqueue(session_logic_handle_->session_event_queue,
+ rcvd_msg_event);
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+
+ return msg_length;
+}
+
+
+/* A function pointer to this function was passed to pcep_socket_comm,
+ * so it will be called when a message is sent. This is useful since
+ * message sending is asynchronous, and there are times that actions
+ * need to be performed only after a message has been sent. */
+void session_logic_message_sent_handler(void *data, int socket_fd)
+{
+ (void)socket_fd;
+
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle msg_sent with NULL data", __func__);
+ return;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+ if (session->destroy_session_after_write == true) {
+ /* Do not call destroy until all of the queued messages are
+ * written */
+ if (session->socket_comm_session != NULL
+ && session->socket_comm_session->message_queue->num_entries
+ == 0) {
+ destroy_pcep_session(session);
+ }
+ } else {
+ /* Reset the keep alive timer for every message sent on
+ * the session, only if the session is not destroyed */
+ if (session->timer_id_keep_alive == TIMER_ID_NOT_SET) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session->session_id);
+ session->timer_id_keep_alive = create_timer(
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ session->session_id);
+ reset_timer(session->timer_id_keep_alive);
+ }
+ }
+}
+
+
+/* A function pointer to this function was passed to pcep_socket_comm,
+ * so it will be called whenever the socket is closed. this function
+ * will be called by the socket_comm thread. */
+void session_logic_conn_except_notifier(void *data, int socket_fd)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot handle conn_except with NULL data",
+ __func__);
+ return;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a connection exception notification while the session logic is not active",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = (pcep_session *)data;
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
+ __func__, time(NULL), pthread_self(), socket_fd,
+ session->session_id);
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ pcep_session_event *socket_event = create_session_event(session);
+ socket_event->socket_closed = true;
+ queue_enqueue(session_logic_handle_->session_event_queue, socket_event);
+ session_logic_handle_->session_logic_condition = true;
+
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+}
+
+
+/*
+ * this method is the timer expire handler, and will only
+ * pass the event to the session_logic loop and notify it
+ * that there is a timer available. this function will be
+ * called by the timers thread.
+ */
+void session_logic_timer_expire_handler(void *data, int timer_id)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
+ __func__);
+ return;
+ }
+
+ if (session_logic_handle_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Received a timer expiration while the session logic is not active",
+ __func__);
+ return;
+ }
+
+ pcep_log(LOG_INFO, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
+ __func__, time(NULL), pthread_self(), timer_id);
+ pcep_session_event *expired_timer_event =
+ create_session_event((pcep_session *)data);
+ expired_timer_event->expired_timer_id = timer_id;
+
+ pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
+ session_logic_handle_->session_logic_condition = true;
+ queue_enqueue(session_logic_handle_->session_event_queue,
+ expired_timer_event);
+
+ pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+}
+
+
+/*
+ * session_logic event loop
+ * this function is called upon thread creation from pcep_session_logic.c
+ */
+void *session_logic_loop(void *data)
+{
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot start session_logic_loop with NULL data",
+ __func__);
+
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_session_logic_handle *session_logic_handle =
+ (pcep_session_logic_handle *)data;
+
+ while (session_logic_handle->active) {
+ /* Mutex locking for session_logic_loop condition variable */
+ pthread_mutex_lock(
+ &(session_logic_handle->session_logic_mutex));
+
+ /* this internal loop helps avoid spurious interrupts */
+ while (!session_logic_handle->session_logic_condition) {
+ pthread_cond_wait(
+ &(session_logic_handle->session_logic_cond_var),
+ &(session_logic_handle->session_logic_mutex));
+ }
+
+ pcep_session_event *event = queue_dequeue(
+ session_logic_handle->session_event_queue);
+ while (event != NULL) {
+ if (event->session == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
+ __func__, time(NULL), pthread_self(),
+ (event->expired_timer_id
+ != TIMER_ID_NOT_SET)
+ ? "timer"
+ : "message");
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle
+ ->session_event_queue);
+ continue;
+ }
+
+ /* Check if the session still exists, and synchronize
+ * possible session destroy */
+ pcep_log(
+ LOG_DEBUG,
+ "%s: session_logic_loop checking session_list sessionPtr %p",
+ __func__, event->session);
+ pthread_mutex_lock(
+ &(session_logic_handle->session_list_mutex));
+ if (ordered_list_find(
+ session_logic_handle->session_list,
+ event->session)
+ == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
+ __func__, time(NULL), pthread_self(),
+ (event->expired_timer_id
+ != TIMER_ID_NOT_SET)
+ ? "timer"
+ : "message");
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle
+ ->session_event_queue);
+ pthread_mutex_unlock(
+ &(session_logic_handle_
+ ->session_list_mutex));
+ continue;
+ }
+
+ if (event->expired_timer_id != TIMER_ID_NOT_SET) {
+ handle_timer_event(event);
+ }
+
+ if (event->received_msg_list != NULL) {
+ handle_socket_comm_event(event);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, event);
+ event = queue_dequeue(
+ session_logic_handle->session_event_queue);
+
+ pthread_mutex_unlock(
+ &(session_logic_handle_->session_list_mutex));
+ }
+
+ session_logic_handle->session_logic_condition = false;
+ pthread_mutex_unlock(
+ &(session_logic_handle->session_logic_mutex));
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
diff --git a/pceplib/pcep_session_logic_states.c b/pceplib/pcep_session_logic_states.c
new file mode 100644
index 0000000000..e2c3c29591
--- /dev/null
+++ b/pceplib/pcep_session_logic_states.c
@@ -0,0 +1,1132 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+#define TIMER_OPEN_KEEP_ALIVE_SECONDS 1
+
+/* Session Logic Handle managed in pcep_session_logic.c */
+extern pcep_event_queue *session_logic_event_queue_;
+void send_keep_alive(pcep_session *session);
+void send_pcep_error_with_object(pcep_session *session,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value,
+ struct pcep_object_header *object);
+void reset_dead_timer(pcep_session *session);
+bool verify_pcep_open_object(pcep_session *session,
+ struct pcep_object_open *open_object);
+void send_reconciled_pcep_open(pcep_session *session,
+ struct pcep_message *error_msg);
+bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg);
+bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg);
+bool check_and_send_open_keep_alive(pcep_session *session);
+void log_pcc_pce_connection(pcep_session *session);
+bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg);
+
+/*
+ * util functions called by the state handling below
+ */
+
+void send_keep_alive(pcep_session *session)
+{
+ struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive();
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send keep_alive message for session [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id);
+
+ session_send_message(session, keep_alive_msg);
+
+ /* The keep alive timer will be (re)set once the message
+ * is sent in session_logic_message_sent_handler() */
+}
+
+
+/* Send an error message with the corrected or offending object */
+void send_pcep_error_with_object(pcep_session *session,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value,
+ struct pcep_object_header *object)
+{
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, object);
+ struct pcep_message *error_msg = pcep_msg_create_error_with_objects(
+ error_type, error_value, obj_list);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send error message with object [%d][%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), error_type, error_value,
+ session->session_id);
+
+ session_send_message(session, error_msg);
+}
+
+
+void send_pcep_error(pcep_session *session, enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ struct pcep_message *error_msg =
+ pcep_msg_create_error(error_type, error_value);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic send error message [%d][%d] for session [%d]",
+ __func__, time(NULL), pthread_self(), error_type, error_value,
+ session->session_id);
+
+ session_send_message(session, error_msg);
+}
+
+
+void reset_dead_timer(pcep_session *session)
+{
+ /* Default to configured dead_timer if its not set yet or set to 0 by
+ * the PCE */
+ int dead_timer_seconds =
+ (session->pcc_config.dead_timer_pce_negotiated_seconds == 0)
+ ? session->pcc_config.dead_timer_seconds
+ : session->pcc_config.dead_timer_pce_negotiated_seconds;
+
+ if (session->timer_id_dead_timer == TIMER_ID_NOT_SET) {
+ session->timer_id_dead_timer =
+ create_timer(dead_timer_seconds, session);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic set dead timer [%d secs] id [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ dead_timer_seconds, session->timer_id_dead_timer,
+ session->session_id);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic reset dead timer [%d secs] id [%d] for session [%d]",
+ __func__, time(NULL), pthread_self(),
+ dead_timer_seconds, session->timer_id_dead_timer,
+ session->session_id);
+ reset_timer(session->timer_id_dead_timer);
+ }
+}
+
+
+void enqueue_event(pcep_session *session, pcep_event_type event_type,
+ struct pcep_message *message)
+{
+ if (event_type == MESSAGE_RECEIVED && message == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: enqueue_event cannot enqueue a NULL message session [%d]",
+ __func__, session->session_id);
+ return;
+ }
+
+ pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event));
+ memset(event, 0, sizeof(pcep_event));
+
+ event->session = session;
+ event->event_type = event_type;
+ event->event_time = time(NULL);
+ event->message = message;
+
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ if (session_logic_event_queue_->event_callback != NULL) {
+ session_logic_event_queue_->event_callback(
+ session_logic_event_queue_->event_callback_data, event);
+ } else {
+ queue_enqueue(session_logic_event_queue_->event_queue, event);
+ }
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+}
+
+/* Verify the received PCEP Open object parameters are acceptable. If not,
+ * update the unacceptable value(s) with an acceptable value so it can be sent
+ * back to the sender. */
+bool verify_pcep_open_object(pcep_session *session,
+ struct pcep_object_open *open_object)
+{
+ int retval = true;
+
+ if (open_object->open_keepalive
+ < session->pcc_config.min_keep_alive_seconds) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open Keep Alive value [%d] min [%d]",
+ __func__, open_object->open_keepalive,
+ session->pcc_config.min_keep_alive_seconds);
+ open_object->open_keepalive =
+ session->pcc_config.min_keep_alive_seconds;
+ retval = false;
+ } else if (open_object->open_keepalive
+ > session->pcc_config.max_keep_alive_seconds) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open Keep Alive value [%d] max [%d]",
+ __func__, open_object->open_keepalive,
+ session->pcc_config.max_keep_alive_seconds);
+ open_object->open_keepalive =
+ session->pcc_config.max_keep_alive_seconds;
+ retval = false;
+ }
+
+ if (open_object->open_deadtimer
+ < session->pcc_config.min_dead_timer_seconds) {
+ pcep_log(LOG_INFO,
+ "%s: Rejecting unsupported Open Dead Timer value [%d]",
+ __func__, open_object->open_deadtimer);
+ open_object->open_deadtimer =
+ session->pcc_config.min_dead_timer_seconds;
+ retval = false;
+ } else if (open_object->open_deadtimer
+ > session->pcc_config.max_dead_timer_seconds) {
+ pcep_log(LOG_INFO,
+ "%s: Rejecting unsupported Open Dead Timer value [%d]",
+ __func__, open_object->open_deadtimer);
+ open_object->open_deadtimer =
+ session->pcc_config.max_dead_timer_seconds;
+ retval = false;
+ }
+
+ /* Check for Open Object TLVs */
+ if (pcep_object_has_tlvs((struct pcep_object_header *)open_object)
+ == false) {
+ /* There are no TLVs, all done */
+ return retval;
+ }
+
+ double_linked_list_node *tlv_node = open_object->header.tlv_list->head;
+ while (tlv_node != NULL) {
+ struct pcep_object_tlv_header *tlv = tlv_node->data;
+ tlv_node = tlv_node->next_node;
+
+ /* Supported Open Object TLVs */
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ break;
+
+ default:
+ /* TODO how to handle unrecognized TLV ?? */
+ pcep_log(
+ LOG_INFO,
+ "%s: Unhandled OPEN Object TLV type: %d, length %d",
+ __func__, tlv->type, tlv->encoded_tlv_length);
+ break;
+ }
+
+ /* Verify the STATEFUL-PCE-CAPABILITY TLV */
+ if (tlv->type == PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY) {
+ struct pcep_object_tlv_stateful_pce_capability
+ *pce_cap_tlv =
+ (struct
+ pcep_object_tlv_stateful_pce_capability
+ *)tlv;
+
+ /* If the U flag is set, then the PCE is
+ * capable of updating LSP parameters */
+ if (pce_cap_tlv->flag_u_lsp_update_capability) {
+ if (session->pce_config
+ .support_stateful_pce_lsp_update
+ == false) {
+ /* Turn off the U bit, as it is not
+ * supported */
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open STATEFUL-PCE-CAPABILITY TLV U flag",
+ __func__);
+ pce_cap_tlv
+ ->flag_u_lsp_update_capability =
+ false;
+ retval = false;
+ } else {
+ session->stateful_pce = true;
+ pcep_log(
+ LOG_INFO,
+ "%s: Setting PCEP session [%d] STATEFUL to support LSP updates",
+ __func__, session->session_id);
+ }
+ }
+ /* TODO the rest of the flags are not implemented yet */
+ else if (pce_cap_tlv->flag_s_include_db_version) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV S Include DB Version flag",
+ __func__);
+ } else if (
+ pce_cap_tlv
+ ->flag_i_lsp_instantiation_capability) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV I LSP Instantiation Capability flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_t_triggered_resync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV T Triggered Resync flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_d_delta_lsp_sync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV D Delta LSP Sync flag",
+ __func__);
+ } else if (pce_cap_tlv->flag_f_triggered_initial_sync) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV F Triggered Initial Sync flag",
+ __func__);
+ }
+ } else if (tlv->type == PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION) {
+ if (session->pce_config.support_include_db_version
+ == false) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Rejecting unsupported Open LSP DB VERSION TLV",
+ __func__);
+ /* Remove this TLV from the list */
+ dll_delete_node(open_object->header.tlv_list,
+ tlv_node);
+ retval = false;
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg)
+{
+ /* Open Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc5440#section-7.15 */
+
+ if (session->session_state != SESSION_STATE_PCEP_CONNECTING
+ && session->session_state != SESSION_STATE_INITIALIZED) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Received unexpected OPEN, current session state [%d, replying with error]",
+ __func__, session->session_state);
+ send_pcep_error(session,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ if (session->pce_open_received == true
+ && session->pce_open_rejected == false) {
+ pcep_log(LOG_INFO,
+ "%s: Received duplicate OPEN, replying with error",
+ __func__);
+ send_pcep_error(session,
+ PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ struct pcep_object_open *open_object =
+ (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+ if (open_object == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Received OPEN message with no OPEN object, replying with error",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ /* Check for additional Open Msg objects */
+ if (open_msg->obj_list->num_entries > 1) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Found additional unsupported objects in the Open message, replying with error",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_INVALID_OPEN_MSG);
+ return false;
+ }
+
+ session->pce_open_received = true;
+
+ /* Verify the open object parameters and TLVs */
+ if (verify_pcep_open_object(session, open_object) == false) {
+ enqueue_event(session, PCC_RCVD_INVALID_OPEN, NULL);
+ if (session->pce_open_rejected) {
+ /* The Open message was already rejected once, so
+ * according to the spec, send an error message and
+ * close the TCP connection. */
+ pcep_log(
+ LOG_INFO,
+ "%s: Received 2 consecutive unsupported Open messages, closing the connection.",
+ __func__);
+ send_pcep_error(
+ session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE);
+ socket_comm_session_close_tcp_after_write(
+ session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+ enqueue_event(session, PCC_CONNECTION_FAILURE, NULL);
+ } else {
+ session->pce_open_rejected = true;
+ /* Clone the object here, since the encapsulating
+ * message will be deleted in handle_socket_comm_event()
+ * most likely before this error message is sent */
+ struct pcep_object_open *cloned_open_object =
+ pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_object_open));
+ memcpy(cloned_open_object, open_object,
+ sizeof(struct pcep_object_open));
+ open_object->header.tlv_list = NULL;
+ cloned_open_object->header.encoded_object = NULL;
+ cloned_open_object->header.encoded_object_length = 0;
+ send_pcep_error_with_object(
+ session, PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG,
+ &cloned_open_object->header);
+ }
+
+ return false;
+ }
+
+ /*
+ * Open Message accepted
+ * Sending the keep-alive response will be managed the function caller
+ */
+
+ session->timer_id_open_keep_alive =
+ create_timer(TIMER_OPEN_KEEP_ALIVE_SECONDS, session);
+ session->pcc_config.dead_timer_pce_negotiated_seconds =
+ (int)open_object->open_deadtimer;
+ /* Cancel the timer so we can change the dead_timer value */
+ cancel_timer(session->timer_id_dead_timer);
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ reset_dead_timer(session);
+
+ return true;
+}
+
+
+/* The original PCEP Open message sent to the PCE was rejected,
+ * try to reconcile the differences and re-send a new Open. */
+void send_reconciled_pcep_open(pcep_session *session,
+ struct pcep_message *error_msg)
+{
+ struct pcep_message *open_msg = create_pcep_open(session);
+
+ struct pcep_object_open *error_open_obj =
+ (struct pcep_object_open *)pcep_obj_get(error_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+ if (error_open_obj == NULL) {
+ /* Nothing to reconcile, send the same Open message again */
+ pcep_log(
+ LOG_INFO,
+ "%s: No Open object received in Error, sending the same Open message",
+ __func__);
+ session_send_message(session, open_msg);
+ return;
+ }
+
+ struct pcep_object_open *open_obj =
+ (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list,
+ PCEP_OBJ_CLASS_OPEN);
+
+ if (error_open_obj->open_deadtimer
+ != session->pce_config.dead_timer_seconds) {
+ if (error_open_obj->open_deadtimer
+ >= session->pce_config.min_dead_timer_seconds
+ && error_open_obj->open_deadtimer
+ <= session->pce_config.max_dead_timer_seconds) {
+ open_obj->open_deadtimer =
+ error_open_obj->open_deadtimer;
+ session->pcc_config.dead_timer_pce_negotiated_seconds =
+ error_open_obj->open_deadtimer;
+ pcep_log(
+ LOG_INFO,
+ "%s: Open deadtimer value [%d] rejected, using PCE value [%d]",
+ __func__,
+ session->pcc_config.dead_timer_seconds,
+ session->pcc_config
+ .dead_timer_pce_negotiated_seconds);
+ /* Reset the timer with the new value */
+ cancel_timer(session->timer_id_dead_timer);
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ reset_dead_timer(session);
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: Can not reconcile Open with suggested deadtimer [%d]",
+ __func__, error_open_obj->open_deadtimer);
+ }
+ }
+
+ if (error_open_obj->open_keepalive
+ != session->pce_config.keep_alive_seconds) {
+ if (error_open_obj->open_keepalive
+ >= session->pce_config.min_keep_alive_seconds
+ && error_open_obj->open_keepalive
+ <= session->pce_config.max_keep_alive_seconds) {
+ open_obj->open_keepalive =
+ error_open_obj->open_keepalive;
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds =
+ error_open_obj->open_keepalive;
+ pcep_log(
+ LOG_INFO,
+ "%s: Open keep alive value [%d] rejected, using PCE value [%d]",
+ __func__,
+ session->pcc_config.keep_alive_seconds,
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds);
+ /* Cancel the timer, the timer will be set again with
+ * the new value when this open message is sent */
+ cancel_timer(session->timer_id_keep_alive);
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ } else {
+ pcep_log(
+ LOG_INFO,
+ "%s: Can not reconcile Open with suggested keepalive [%d]",
+ __func__, error_open_obj->open_keepalive);
+ }
+ }
+
+ /* TODO reconcile the TLVs */
+
+ session_send_message(session, open_msg);
+ reset_timer(session->timer_id_open_keep_alive);
+}
+
+
+bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg)
+{
+ /* Update Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc8231#section-6.2 */
+
+ if (upd_msg->obj_list == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Message has no objects",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the mandatory objects are present */
+ struct pcep_object_header *obj =
+ pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_SRP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing SRP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_LSP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing LSP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_ERO);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcUpd message: Missing ERO object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_ERO_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the objects are are in the correct order */
+ double_linked_list_node *node = upd_msg->obj_list->head;
+ struct pcep_object_srp *srp_object =
+ (struct pcep_object_srp *)node->data;
+ if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: First object must be an SRP, found [%d]",
+ __func__, srp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_lsp *lsp_object =
+ (struct pcep_object_lsp *)node->data;
+ if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: Second object must be an LSP, found [%d]",
+ __func__, lsp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_ro *ero_object = node->data;
+ if (ero_object->header.object_class != PCEP_OBJ_CLASS_ERO) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcUpd message: Third object must be an ERO, found [%d]",
+ __func__, ero_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_ERO_OBJECT_MISSING);
+ return false;
+ }
+
+ return true;
+}
+
+bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg)
+{
+ /* Instantiate Message validation and errors according to:
+ * https://tools.ietf.org/html/rfc8281#section-5 */
+
+ if (init_msg->obj_list == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: Message has no objects",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the mandatory objects are present */
+ struct pcep_object_header *obj =
+ pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_SRP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcInitiate message: Missing SRP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ obj = pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_LSP);
+ if (obj == NULL) {
+ pcep_log(LOG_INFO,
+ "%s: Invalid PcInitiate message: Missing LSP object",
+ __func__);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* Verify the objects are are in the correct order */
+ double_linked_list_node *node = init_msg->obj_list->head;
+ struct pcep_object_srp *srp_object =
+ (struct pcep_object_srp *)node->data;
+ if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: First object must be an SRP, found [%d]",
+ __func__, srp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING);
+ return false;
+ }
+
+ node = node->next_node;
+ struct pcep_object_lsp *lsp_object =
+ (struct pcep_object_lsp *)node->data;
+ if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Invalid PcInitiate message: Second object must be an LSP, found [%d]",
+ __func__, lsp_object->header.object_class);
+ send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING);
+ return false;
+ }
+
+ /* There may be more optional objects */
+ return true;
+}
+
+void increment_unknown_message(pcep_session *session)
+{
+ /* https://tools.ietf.org/html/rfc5440#section-6.9
+ * If a PCC/PCE receives unrecognized messages at a rate equal or
+ * greater than MAX-UNKNOWN-MESSAGES unknown message requests per
+ * minute, the PCC/PCE MUST send a PCEP CLOSE message */
+
+ time_t *unknown_message_time =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(time_t));
+ *unknown_message_time = time(NULL);
+ time_t expire_time = *unknown_message_time + 60;
+ queue_enqueue(session->num_unknown_messages_time_queue,
+ unknown_message_time);
+
+ /* Purge any entries older than 1 minute. The oldest entries are at the
+ * queue head */
+ queue_node *time_node = session->num_unknown_messages_time_queue->head;
+ while (time_node != NULL) {
+ if (*((time_t *)time_node->data) > expire_time) {
+ pceplib_free(
+ PCEPLIB_INFRA,
+ queue_dequeue(
+ session->num_unknown_messages_time_queue));
+ time_node =
+ session->num_unknown_messages_time_queue->head;
+ } else {
+ time_node = NULL;
+ }
+ }
+
+ if ((int)session->num_unknown_messages_time_queue->num_entries
+ >= session->pcc_config.max_unknown_messages) {
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Max unknown messages reached [%d] closing session [%d]",
+ __func__, time(NULL), pthread_self(),
+ session->pcc_config.max_unknown_messages,
+ session->session_id);
+
+ close_pcep_session_with_reason(session,
+ PCEP_CLOSE_REASON_UNREC_MSG);
+ enqueue_event(session, PCC_RCVD_MAX_UNKOWN_MSGS, NULL);
+ }
+}
+
+bool check_and_send_open_keep_alive(pcep_session *session)
+{
+ if (session->pce_open_received == true
+ && session->pce_open_rejected == false
+ && session->pce_open_keep_alive_sent == false) {
+ /* Send the PCE Open keep-alive response if it hasnt been sent
+ * yet */
+ cancel_timer(session->timer_id_open_keep_alive);
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ send_keep_alive(session);
+ session->pce_open_keep_alive_sent = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+void log_pcc_pce_connection(pcep_session *session)
+{
+ if (session->socket_comm_session == NULL) {
+ /* This only happens in UT */
+ return;
+ }
+
+ char src_ip_buf[40] = {0}, dst_ip_buf[40] = {0};
+ uint16_t src_port, dst_port;
+
+ if (session->socket_comm_session->is_ipv6) {
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6.sin6_addr,
+ src_ip_buf, sizeof(src_ip_buf));
+ inet_ntop(AF_INET6,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_addr,
+ dst_ip_buf, sizeof(dst_ip_buf));
+ src_port = htons(session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6.sin6_port);
+ dst_port = htons(session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6.sin6_port);
+ } else {
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4.sin_addr,
+ src_ip_buf, sizeof(src_ip_buf));
+ inet_ntop(AF_INET,
+ &session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_addr,
+ dst_ip_buf, sizeof(dst_ip_buf));
+ src_port = htons(session->socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4.sin_port);
+ dst_port = htons(session->socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4.sin_port);
+ }
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Successful PCC [%s:%d] connection to PCE [%s:%d] session [%d] fd [%d]",
+ __func__, time(NULL), pthread_self(), src_ip_buf, src_port,
+ dst_ip_buf, dst_port, session->session_id,
+ session->socket_comm_session->socket_fd);
+}
+
+/*
+ * these functions are called by session_logic_loop() from
+ * pcep_session_logic_loop.c these functions are executed in the
+ * session_logic_loop thread, and the mutex is locked before calling these
+ * functions, so they are thread safe.
+ */
+
+/* state machine handling for expired timers */
+void handle_timer_event(pcep_session_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_INFO, "%s: handle_timer_event NULL event",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = event->session;
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic handle_timer_event: session [%d] event timer_id [%d] session timers [OKW, OKA, DT, KA] [%d, %d, %d, %d]",
+ __func__, time(NULL), pthread_self(), session->session_id,
+ event->expired_timer_id, session->timer_id_open_keep_wait,
+ session->timer_id_open_keep_alive, session->timer_id_dead_timer,
+ session->timer_id_keep_alive);
+
+ /*
+ * these timer expirations are independent of the session state
+ */
+ if (event->expired_timer_id == session->timer_id_dead_timer) {
+ session->timer_id_dead_timer = TIMER_ID_NOT_SET;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER);
+ close_pcep_session_with_reason(session,
+ PCEP_CLOSE_REASON_DEADTIMER);
+ enqueue_event(session, PCE_DEAD_TIMER_EXPIRED, NULL);
+ return;
+ } else if (event->expired_timer_id == session->timer_id_keep_alive) {
+ session->timer_id_keep_alive = TIMER_ID_NOT_SET;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE);
+ send_keep_alive(session);
+ return;
+ }
+
+ /*
+ * handle timers that depend on the session state
+ */
+ switch (session->session_state) {
+ case SESSION_STATE_PCEP_CONNECTING:
+ if (event->expired_timer_id
+ == session->timer_id_open_keep_wait) {
+ /* close the TCP session */
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_timer_event open_keep_wait timer expired for session [%d]",
+ __func__, session->session_id);
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT);
+ socket_comm_session_close_tcp_after_write(
+ session->socket_comm_session);
+ session->session_state = SESSION_STATE_INITIALIZED;
+ session->timer_id_open_keep_wait = TIMER_ID_NOT_SET;
+ enqueue_event(session, PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED,
+ NULL);
+ }
+
+ if (event->expired_timer_id
+ == session->timer_id_open_keep_alive) {
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE);
+ session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
+ if (check_and_send_open_keep_alive(session) == true) {
+ if (session->pcc_open_accepted == true
+ && session->session_state
+ != SESSION_STATE_PCEP_CONNECTED) {
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ return;
+ }
+ break;
+
+ case SESSION_STATE_INITIALIZED:
+ case SESSION_STATE_PCEP_CONNECTED:
+ default:
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_timer_event unrecognized state transition, timer_id [%d] state [%d] session [%d]",
+ __func__, event->expired_timer_id,
+ session->session_state, session->session_id);
+ break;
+ }
+}
+
+/* State machine handling for received messages.
+ * This event was created in session_logic_msg_ready_handler() in
+ * pcep_session_logic_loop.c */
+void handle_socket_comm_event(pcep_session_event *event)
+{
+ if (event == NULL) {
+ pcep_log(LOG_INFO, "%s: handle_socket_comm_event NULL event",
+ __func__);
+ return;
+ }
+
+ pcep_session *session = event->session;
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] pcep_session_logic handle_socket_comm_event: session [%d] num messages [%d] socket_closed [%d]",
+ __func__, time(NULL), pthread_self(), session->session_id,
+ (event->received_msg_list == NULL
+ ? -1
+ : (int)event->received_msg_list->num_entries),
+ event->socket_closed);
+
+ /*
+ * independent of the session state
+ */
+ if (event->socket_closed) {
+ pcep_log(
+ LOG_INFO,
+ "%s: handle_socket_comm_event socket closed for session [%d]",
+ __func__, session->session_id);
+ socket_comm_session_close_tcp(session->socket_comm_session);
+ enqueue_event(session, PCE_CLOSED_SOCKET, NULL);
+ if (session->session_state == SESSION_STATE_PCEP_CONNECTING) {
+ enqueue_event(session, PCC_CONNECTION_FAILURE, NULL);
+ }
+ session->session_state = SESSION_STATE_INITIALIZED;
+ increment_event_counters(session,
+ PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT);
+ return;
+ }
+
+ reset_dead_timer(session);
+
+ if (event->received_msg_list == NULL) {
+ return;
+ }
+
+ /* Message received on socket */
+ double_linked_list_node *msg_node;
+ for (msg_node = event->received_msg_list->head; msg_node != NULL;
+ msg_node = msg_node->next_node) {
+ bool message_enqueued = false;
+ struct pcep_message *msg =
+ (struct pcep_message *)msg_node->data;
+ pcep_log(LOG_INFO, "%s: \t %s message", __func__,
+ get_message_type_str(msg->msg_header->type));
+
+ increment_message_rx_counters(session, msg);
+
+ switch (msg->msg_header->type) {
+ case PCEP_TYPE_OPEN:
+ /* handle_pcep_open() checks session state, and for
+ * duplicate erroneous open messages, and replies with
+ * error messages as needed. It also sets
+ * pce_open_received. */
+ if (handle_pcep_open(session, msg) == true) {
+ /* PCE Open Message Accepted */
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ session->pce_open_accepted = true;
+ session->pce_open_rejected = false;
+ if (session->pcc_open_accepted) {
+ /* If both the PCC and PCE Opens are
+ * accepted, then the session is
+ * connected */
+
+ check_and_send_open_keep_alive(session);
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCE_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ break;
+
+ case PCEP_TYPE_KEEPALIVE:
+ if (session->session_state
+ == SESSION_STATE_PCEP_CONNECTING) {
+ /* PCC Open Message Accepted */
+ cancel_timer(session->timer_id_open_keep_wait);
+ session->timer_id_open_keep_wait =
+ TIMER_ID_NOT_SET;
+ session->pcc_open_accepted = true;
+ session->pcc_open_rejected = false;
+ check_and_send_open_keep_alive(session);
+
+ if (session->pce_open_accepted) {
+ /* If both the PCC and PCE Opens are
+ * accepted, then the session is
+ * connected */
+ log_pcc_pce_connection(session);
+ session->session_state =
+ SESSION_STATE_PCEP_CONNECTED;
+ increment_event_counters(
+ session,
+ PCEP_EVENT_COUNTER_ID_PCC_CONNECT);
+ enqueue_event(session,
+ PCC_CONNECTED_TO_PCE,
+ NULL);
+ }
+ }
+ /* The dead_timer was already reset above, so nothing
+ * extra to do here */
+ break;
+
+ case PCEP_TYPE_PCREP:
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ case PCEP_TYPE_CLOSE:
+ session->session_state = SESSION_STATE_INITIALIZED;
+ socket_comm_session_close_tcp(
+ session->socket_comm_session);
+ /* TODO should we also enqueue the message, so they can
+ * see the reasons?? */
+ enqueue_event(session, PCE_SENT_PCEP_CLOSE, NULL);
+ /* TODO could this duplicate the disconnect counter with
+ * socket close ?? */
+ increment_event_counters(
+ session, PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT);
+ break;
+
+ case PCEP_TYPE_PCREQ:
+ /* The PCC does not support receiving PcReq messages */
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ break;
+
+ case PCEP_TYPE_REPORT:
+ /* The PCC does not support receiving Report messages */
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ break;
+
+ case PCEP_TYPE_UPDATE:
+ /* Should reply with a PcRpt */
+ if (handle_pcep_update(session, msg) == true) {
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ }
+ break;
+
+ case PCEP_TYPE_INITIATE:
+ /* Should reply with a PcRpt */
+ if (handle_pcep_initiate(session, msg) == true) {
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ }
+ break;
+
+ case PCEP_TYPE_PCNOTF:
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ case PCEP_TYPE_ERROR:
+ if (msg->obj_list != NULL
+ && msg->obj_list->num_entries > 0) {
+ struct pcep_object_header *obj_hdr =
+ pcep_obj_get(msg->obj_list,
+ PCEP_OBJ_CLASS_ERROR);
+ if (obj_hdr != NULL) {
+ struct pcep_object_error *error_obj =
+ (struct pcep_object_error *)
+ obj_hdr;
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Error object [type, value] = [%s, %s]",
+ __func__,
+ get_error_type_str(
+ error_obj->error_type),
+ get_error_value_str(
+ error_obj->error_type,
+ error_obj
+ ->error_value));
+ }
+ }
+
+ if (session->session_state
+ == SESSION_STATE_PCEP_CONNECTING) {
+ /* A PCC_CONNECTION_FAILURE event will be sent
+ * when the socket is closed, if the state is
+ * SESSION_STATE_PCEP_CONNECTING, in case the
+ * PCE allows more than 2 failed open messages.
+ */
+ pcep_log(LOG_INFO,
+ "%s: PCC Open message rejected by PCE",
+ __func__);
+ session->pcc_open_rejected = true;
+ send_reconciled_pcep_open(session, msg);
+ enqueue_event(session, PCC_SENT_INVALID_OPEN,
+ NULL);
+ }
+ enqueue_event(session, MESSAGE_RECEIVED, msg);
+ message_enqueued = true;
+ break;
+
+ default:
+ pcep_log(LOG_INFO, "%s: \t UnSupported message",
+ __func__);
+ send_pcep_error(session,
+ PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ PCEP_ERRV_UNASSIGNED);
+ increment_unknown_message(session);
+ break;
+ }
+
+ /* if the message was enqueued, dont free it yet */
+ if (message_enqueued == false) {
+ pcep_msg_free_message(msg);
+ }
+ }
+ dll_destroy(event->received_msg_list);
+}
diff --git a/pceplib/pcep_socket_comm.c b/pceplib/pcep_socket_comm.c
new file mode 100644
index 0000000000..e22eb6e675
--- /dev/null
+++ b/pceplib/pcep_socket_comm.c
@@ -0,0 +1,781 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of public API functions.
+ */
+
+#include <zebra.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h> // gethostbyname
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h> // close
+
+#include <arpa/inet.h> // sockets etc.
+#include <sys/types.h> // sockets etc.
+#include <sys/socket.h> // sockets etc.
+
+#include "pcep.h"
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_internals.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_queue.h"
+
+bool initialize_socket_comm_pre(void);
+bool socket_comm_session_initialize_post(
+ pcep_socket_comm_session *socket_comm_session);
+
+pcep_socket_comm_handle *socket_comm_handle_ = NULL;
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * for ordered list insertion. */
+int socket_fd_node_compare(void *list_entry, void *new_entry)
+{
+ return ((pcep_socket_comm_session *)new_entry)->socket_fd
+ - ((pcep_socket_comm_session *)list_entry)->socket_fd;
+}
+
+
+bool initialize_socket_comm_pre()
+{
+ socket_comm_handle_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle));
+ memset(socket_comm_handle_, 0, sizeof(pcep_socket_comm_handle));
+
+ socket_comm_handle_->active = true;
+ socket_comm_handle_->num_active_sessions = 0;
+ socket_comm_handle_->read_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ socket_comm_handle_->write_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ socket_comm_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ FD_ZERO(&socket_comm_handle_->except_master_set);
+ FD_ZERO(&socket_comm_handle_->read_master_set);
+ FD_ZERO(&socket_comm_handle_->write_master_set);
+
+ if (pthread_mutex_init(&(socket_comm_handle_->socket_comm_mutex), NULL)
+ != 0) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm mutex.",
+ __func__);
+ pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
+ socket_comm_handle_ = NULL;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func)
+{
+ if (socket_comm_handle_ != NULL) {
+ /* already initialized */
+ return true;
+ }
+
+ if (initialize_socket_comm_pre() == false) {
+ return false;
+ }
+
+ /* Notice: If the thread_create_func is set, then both the
+ * socket_read_cb and the socket_write_cb SHOULD be NULL. */
+ if (thread_create_func != NULL) {
+ if (thread_create_func(
+ &(socket_comm_handle_->socket_comm_thread), NULL,
+ socket_comm_loop, socket_comm_handle_,
+ "pceplib_timers")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external socket_comm thread.",
+ __func__);
+ return false;
+ }
+ }
+
+ socket_comm_handle_->external_infra_data = external_infra_data;
+ socket_comm_handle_->socket_write_func = socket_write_cb;
+ socket_comm_handle_->socket_read_func = socket_read_cb;
+
+ return true;
+}
+
+bool initialize_socket_comm_loop()
+{
+ if (socket_comm_handle_ != NULL) {
+ /* already initialized */
+ return true;
+ }
+
+ if (initialize_socket_comm_pre() == false) {
+ return false;
+ }
+
+ /* Launch socket comm loop pthread */
+ if (pthread_create(&(socket_comm_handle_->socket_comm_thread), NULL,
+ socket_comm_loop, socket_comm_handle_)) {
+ pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm thread.",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool destroy_socket_comm_loop()
+{
+ socket_comm_handle_->active = false;
+
+ pthread_join(socket_comm_handle_->socket_comm_thread, NULL);
+ ordered_list_destroy(socket_comm_handle_->read_list);
+ ordered_list_destroy(socket_comm_handle_->write_list);
+ ordered_list_destroy(socket_comm_handle_->session_list);
+ pthread_mutex_destroy(&(socket_comm_handle_->socket_comm_mutex));
+
+ pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
+ socket_comm_handle_ = NULL;
+
+ return true;
+}
+
+/* Internal common init function */
+static pcep_socket_comm_session *socket_comm_session_initialize_pre(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ /* check that not both message handlers were set */
+ if (message_handler != NULL && message_ready_handler != NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Only one of <message_received_handler | message_ready_to_read_handler> can be set.",
+ __func__);
+ return NULL;
+ }
+
+ /* check that at least one message handler was set */
+ if (message_handler == NULL && message_ready_handler == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: At least one of <message_received_handler | message_ready_to_read_handler> must be set.",
+ __func__);
+ return NULL;
+ }
+
+ if (!initialize_socket_comm_loop()) {
+ pcep_log(LOG_WARNING,
+ "%s: ERROR: cannot initialize socket_comm_loop.",
+ __func__);
+
+ return NULL;
+ }
+
+ /* initialize everything for a pcep_session socket_comm */
+
+ pcep_socket_comm_session *socket_comm_session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session));
+ memset(socket_comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ socket_comm_handle_->num_active_sessions++;
+ socket_comm_session->close_after_write = false;
+ socket_comm_session->session_data = session_data;
+ socket_comm_session->message_handler = message_handler;
+ socket_comm_session->message_ready_to_read_handler =
+ message_ready_handler;
+ socket_comm_session->message_sent_handler = msg_sent_notifier;
+ socket_comm_session->conn_except_notifier = notifier;
+ socket_comm_session->message_queue = queue_initialize();
+ socket_comm_session->connect_timeout_millis = connect_timeout_millis;
+ socket_comm_session->external_socket_data = NULL;
+ if (tcp_authentication_str != NULL) {
+ socket_comm_session->is_tcp_auth_md5 = is_tcp_auth_md5;
+ strlcpy(socket_comm_session->tcp_authentication_str,
+ tcp_authentication_str,
+ sizeof(socket_comm_session->tcp_authentication_str));
+ }
+
+ return socket_comm_session;
+}
+
+/* Internal common init function */
+bool socket_comm_session_initialize_post(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ /* If we dont use SO_REUSEADDR, the socket will take 2 TIME_WAIT
+ * periods before being closed in the kernel if bind() was called */
+ int reuse_addr = 1;
+ if (setsockopt(socket_comm_session->socket_fd, SOL_SOCKET, SO_REUSEADDR,
+ &reuse_addr, sizeof(int))
+ < 0) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Error in setsockopt() SO_REUSEADDR errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(socket_comm_session);
+
+ return false;
+ }
+
+ struct sockaddr *src_sock_addr =
+ (socket_comm_session->is_ipv6
+ ? (struct sockaddr *)&(
+ socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6)
+ : (struct sockaddr *)&(
+ socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4));
+ int addr_len = (socket_comm_session->is_ipv6
+ ? sizeof(socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv6)
+ : sizeof(socket_comm_session->src_sock_addr
+ .src_sock_addr_ipv4));
+ if (bind(socket_comm_session->socket_fd, src_sock_addr, addr_len)
+ == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot bind address to socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(socket_comm_session);
+
+ return false;
+ }
+
+ /* Register the session as active with the Socket Comm Loop */
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ ordered_list_add_node(socket_comm_handle_->session_list,
+ socket_comm_session);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ /* dont connect to the destination yet, since the PCE will have a timer
+ * for max time between TCP connect and PCEP open. we'll connect later
+ * when we send the PCEP open. */
+
+ return true;
+}
+
+
+pcep_socket_comm_session *socket_comm_session_initialize(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *dest_ip,
+ short dest_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ return socket_comm_session_initialize_with_src(
+ message_handler, message_ready_handler, msg_sent_notifier,
+ notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dest_ip,
+ short dest_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ return socket_comm_session_initialize_with_src_ipv6(
+ message_handler, message_ready_handler, msg_sent_notifier,
+ notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+}
+
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dest_ip, short dest_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ if (dest_ip == NULL) {
+ pcep_log(LOG_WARNING, "%s: dest_ipv4 is NULL", __func__);
+ return NULL;
+ }
+
+ pcep_socket_comm_session *socket_comm_session =
+ socket_comm_session_initialize_pre(
+ message_handler, message_ready_handler,
+ msg_sent_notifier, notifier, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+ if (socket_comm_session == NULL) {
+ return NULL;
+ }
+
+ socket_comm_session->socket_fd =
+ socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (socket_comm_session->socket_fd == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create ipv4 socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(
+ socket_comm_session); // socket_comm_session freed
+ // inside fn so NOLINT next.
+
+ return NULL; // NOLINT(clang-analyzer-unix.Malloc)
+ }
+
+ socket_comm_session->is_ipv6 = false;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family =
+ AF_INET;
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family =
+ AF_INET;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dest_port);
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port =
+ htons(src_port);
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr
+ .s_addr = dest_ip->s_addr;
+ if (src_ip != NULL) {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
+ .s_addr = src_ip->s_addr;
+ } else {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
+ .s_addr = INADDR_ANY;
+ }
+
+ if (socket_comm_session_initialize_post(socket_comm_session) == false) {
+ return NULL;
+ }
+
+ return socket_comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler message_handler,
+ message_ready_to_read_handler message_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dest_ip, short dest_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ if (dest_ip == NULL) {
+ pcep_log(LOG_WARNING, "%s: dest_ipv6 is NULL", __func__);
+ return NULL;
+ }
+
+ pcep_socket_comm_session *socket_comm_session =
+ socket_comm_session_initialize_pre(
+ message_handler, message_ready_handler,
+ msg_sent_notifier, notifier, connect_timeout_millis,
+ tcp_authentication_str, is_tcp_auth_md5, session_data);
+ if (socket_comm_session == NULL) {
+ return NULL;
+ }
+
+ socket_comm_session->socket_fd =
+ socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (socket_comm_session->socket_fd == -1) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot create ipv6 socket errno [%d %s].",
+ __func__, errno, strerror(errno));
+ socket_comm_session_teardown(
+ socket_comm_session); // socket_comm_session freed
+ // inside fn so NOLINT next.
+
+ return NULL; // NOLINT(clang-analyzer-unix.Malloc)
+ }
+
+ socket_comm_session->is_ipv6 = true;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family =
+ AF_INET6;
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family =
+ AF_INET6;
+ socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dest_port);
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port =
+ htons(src_port);
+ memcpy(&socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6
+ .sin6_addr,
+ dest_ip, sizeof(struct in6_addr));
+ if (src_ip != NULL) {
+ memcpy(&socket_comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ src_ip, sizeof(struct in6_addr));
+ } else {
+ socket_comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr = in6addr_any;
+ }
+
+ if (socket_comm_session_initialize_post(socket_comm_session) == false) {
+ return NULL;
+ }
+
+ return socket_comm_session;
+}
+
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_connect_tcp NULL socket_comm_session.",
+ __func__);
+ return NULL;
+ }
+
+ /* Set the socket to non-blocking, so connect() does not block */
+ int fcntl_arg;
+ if ((fcntl_arg = fcntl(socket_comm_session->socket_fd, F_GETFL, NULL))
+ < 0) {
+ pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_GETFL) [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+
+ fcntl_arg |= O_NONBLOCK;
+ if (fcntl(socket_comm_session->socket_fd, F_SETFL, fcntl_arg) < 0) {
+ pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_SETFL) [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+
+#if HAVE_DECL_TCP_MD5SIG
+ /* TCP authentication, currently only TCP MD5 RFC2385 is supported */
+ if (socket_comm_session->tcp_authentication_str[0] != '\0') {
+#if defined(linux) || defined(GNU_LINUX)
+ struct tcp_md5sig sig;
+ memset(&sig, 0, sizeof(sig));
+ if (socket_comm_session->is_ipv6) {
+ memcpy(&sig.tcpm_addr,
+ &socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6,
+ sizeof(struct sockaddr_in6));
+ } else {
+ memcpy(&sig.tcpm_addr,
+ &socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4,
+ sizeof(struct sockaddr_in));
+ }
+ sig.tcpm_keylen =
+ strlen(socket_comm_session->tcp_authentication_str);
+ memcpy(sig.tcpm_key,
+ socket_comm_session->tcp_authentication_str,
+ sig.tcpm_keylen);
+#else
+ int sig = 1;
+#endif
+ if (setsockopt(socket_comm_session->socket_fd, IPPROTO_TCP,
+ TCP_MD5SIG, &sig, sizeof(sig))
+ == -1) {
+ pcep_log(LOG_ERR, "%s: Failed to setsockopt(): [%d %s]",
+ __func__, errno, strerror(errno));
+ return false;
+ }
+ }
+#endif
+
+ int connect_result = 0;
+ if (socket_comm_session->is_ipv6) {
+ connect_result = connect(
+ socket_comm_session->socket_fd,
+ (struct sockaddr *)&(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6),
+ sizeof(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv6));
+ } else {
+ connect_result = connect(
+ socket_comm_session->socket_fd,
+ (struct sockaddr *)&(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4),
+ sizeof(socket_comm_session->dest_sock_addr
+ .dest_sock_addr_ipv4));
+ }
+
+ if (connect_result < 0) {
+ if (errno == EINPROGRESS) {
+ /* Calculate the configured timeout in seconds and
+ * microseconds */
+ struct timeval tv;
+ if (socket_comm_session->connect_timeout_millis
+ > 1000) {
+ tv.tv_sec = socket_comm_session
+ ->connect_timeout_millis
+ / 1000;
+ tv.tv_usec = (socket_comm_session
+ ->connect_timeout_millis
+ - (tv.tv_sec * 1000))
+ * 1000;
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = socket_comm_session
+ ->connect_timeout_millis
+ * 1000;
+ }
+
+ /* Use select to wait a max timeout for connect
+ * https://stackoverflow.com/questions/2597608/c-socket-connection-timeout
+ */
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(socket_comm_session->socket_fd, &fdset);
+ if (select(socket_comm_session->socket_fd + 1, NULL,
+ &fdset, NULL, &tv)
+ > 0) {
+ int so_error;
+ socklen_t len = sizeof(so_error);
+ getsockopt(socket_comm_session->socket_fd,
+ SOL_SOCKET, SO_ERROR, &so_error,
+ &len);
+ if (so_error) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect failed on socket_fd [%d].",
+ __func__,
+ socket_comm_session->socket_fd);
+ return false;
+ }
+ } else {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect timed-out on socket_fd [%d].",
+ __func__,
+ socket_comm_session->socket_fd);
+ return false;
+ }
+ } else {
+ pcep_log(
+ LOG_WARNING,
+ "%s: TCP connect, error connecting on socket_fd [%d] errno [%d %s]",
+ __func__, socket_comm_session->socket_fd, errno,
+ strerror(errno));
+ return false;
+ }
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ /* once the TCP connection is open, we should be ready to read at any
+ * time */
+ ordered_list_add_node(socket_comm_handle_->read_list,
+ socket_comm_session);
+
+ if (socket_comm_handle_->socket_read_func != NULL) {
+ socket_comm_handle_->socket_read_func(
+ socket_comm_handle_->external_infra_data,
+ &socket_comm_session->external_socket_data,
+ socket_comm_session->socket_fd, socket_comm_handle_);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_close_tcp NULL socket_comm_session.",
+ __func__);
+ return false;
+ }
+
+ pcep_log(LOG_DEBUG,
+ "%s: socket_comm_session_close_tcp close() socket fd [%d]",
+ __func__, socket_comm_session->socket_fd);
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
+ socket_comm_session);
+ // TODO should it be close() or shutdown()??
+ close(socket_comm_session->socket_fd);
+ socket_comm_session->socket_fd = -1;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_close_tcp_after_write NULL socket_comm_session.",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ socket_comm_session->close_after_write = true;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ return true;
+}
+
+
+bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle_ == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot teardown NULL socket_comm_handle",
+ __func__);
+ return false;
+ }
+
+ if (socket_comm_session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Cannot teardown NULL session",
+ __func__);
+ return false;
+ }
+
+ if (comm_session_exists_locking(socket_comm_handle_,
+ socket_comm_session)
+ == false) {
+ pcep_log(LOG_WARNING,
+ "%s: Cannot teardown session that does not exist",
+ __func__);
+ return false;
+ }
+
+ if (socket_comm_session->socket_fd >= 0) {
+ shutdown(socket_comm_session->socket_fd, SHUT_RDWR);
+ close(socket_comm_session->socket_fd);
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+ queue_destroy(socket_comm_session->message_queue);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->session_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
+ socket_comm_session);
+ ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
+ socket_comm_session);
+ socket_comm_handle_->num_active_sessions--;
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm_session fd [%d] destroyed, [%d] sessions remaining",
+ __func__, time(NULL), pthread_self(),
+ socket_comm_session->socket_fd,
+ socket_comm_handle_->num_active_sessions);
+
+ pceplib_free(PCEPLIB_INFRA, socket_comm_session);
+
+ /* It would be nice to call destroy_socket_comm_loop() here if
+ * socket_comm_handle_->num_active_sessions == 0, but this function
+ * will usually be called from the message_sent_notifier callback,
+ * which gets called in the middle of the socket_comm_loop, and that
+ * is dangerous, so destroy_socket_comm_loop() must be called upon
+ * application exit. */
+
+ return true;
+}
+
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool free_after_send)
+{
+ if (socket_comm_session == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: socket_comm_session_send_message NULL socket_comm_session.",
+ __func__);
+ return;
+ }
+
+ pcep_socket_comm_queued_message *queued_message = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(pcep_socket_comm_queued_message));
+ queued_message->encoded_message = encoded_message;
+ queued_message->msg_length = msg_length;
+ queued_message->free_after_send = free_after_send;
+
+ pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
+
+ /* Do not proceed if the socket_comm_session has been deleted */
+ if (ordered_list_find(socket_comm_handle_->session_list,
+ socket_comm_session)
+ == NULL) {
+ /* Should never get here, only if the session was deleted and
+ * someone still tries to write on it */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot write a message on a deleted socket comm session, discarding message",
+ __func__);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+
+ return;
+ }
+
+ /* Do not proceed if the socket has been closed */
+ if (socket_comm_session->socket_fd < 0) {
+ /* Should never get here, only if the session was deleted and
+ * someone still tries to write on it */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot write a message on a closed socket, discarding message",
+ __func__);
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+
+ return;
+ }
+
+ queue_enqueue(socket_comm_session->message_queue, queued_message);
+
+ /* Add it to the write list only if its not already there */
+ if (ordered_list_find(socket_comm_handle_->write_list,
+ socket_comm_session)
+ == NULL) {
+ ordered_list_add_node(socket_comm_handle_->write_list,
+ socket_comm_session);
+ }
+
+ if (socket_comm_handle_->socket_write_func != NULL) {
+ socket_comm_handle_->socket_write_func(
+ socket_comm_handle_->external_infra_data,
+ &socket_comm_session->external_socket_data,
+ socket_comm_session->socket_fd, socket_comm_handle_);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
+}
diff --git a/pceplib/pcep_socket_comm.h b/pceplib/pcep_socket_comm.h
new file mode 100644
index 0000000000..797ffda860
--- /dev/null
+++ b/pceplib/pcep_socket_comm.h
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Declaration of public API functions.
+ */
+
+#ifndef INCLUDE_PCEPSOCKETCOMM_H_
+#define INCLUDE_PCEPSOCKETCOMM_H_
+
+#include "pcep.h"
+#include <arpa/inet.h> // sockaddr_in
+#include <netinet/tcp.h>
+#include <stdbool.h>
+
+#include "pcep_utils_queue.h"
+
+#define MAX_RECVD_MSG_SIZE 2048
+
+/*
+ * A socket_comm_session can be initialized with 1 of 2 types of mutually
+ * exclusive message callbacks:
+ * - message_received_handler : the socket_comm library reads the message and
+ * calls the callback with the message_data and message_length. this callback
+ * should be used for smaller/simpler messages.
+ * - message_ready_to_read_handler : the socket_comm library will call this
+ * callback when a message is ready to be read on a socket_fd. this callback
+ * should be used if the
+ */
+
+/* message received handler that receives the message data and message length */
+typedef void (*message_received_handler)(void *session_data,
+ const char *message_data,
+ unsigned int message_length);
+/* message ready received handler that should read the message on socket_fd
+ * and return the number of bytes read */
+typedef int (*message_ready_to_read_handler)(void *session_data, int socket_fd);
+/* callback handler called when a messages is sent */
+typedef void (*message_sent_notifier)(void *session_data, int socket_fd);
+/* callback handler called when the socket is closed */
+typedef void (*connection_except_notifier)(void *session_data, int socket_fd);
+
+/* Function pointers when an external socket infrastructure is used */
+typedef int (*ext_socket_write)(void *infra_data, void **infra_socket_data,
+ int fd, void *data);
+typedef int (*ext_socket_read)(void *infra_data, void **infra_socket_data,
+ int fd, void *data);
+typedef int (*ext_socket_pthread_create_callback)(
+ pthread_t *pthread_id, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *data, const char *thread_name);
+
+typedef struct pcep_socket_comm_session_ {
+ message_received_handler message_handler;
+ message_ready_to_read_handler message_ready_to_read_handler;
+ message_sent_notifier message_sent_handler;
+ connection_except_notifier conn_except_notifier;
+ union src_sock_addr {
+ struct sockaddr_in src_sock_addr_ipv4;
+ struct sockaddr_in6 src_sock_addr_ipv6;
+ } src_sock_addr;
+ union dest_sock_addr {
+ struct sockaddr_in dest_sock_addr_ipv4;
+ struct sockaddr_in6 dest_sock_addr_ipv6;
+ } dest_sock_addr;
+ bool is_ipv6;
+ uint32_t connect_timeout_millis;
+ int socket_fd;
+ void *session_data;
+ queue_handle *message_queue;
+ char received_message[MAX_RECVD_MSG_SIZE];
+ int received_bytes;
+ bool close_after_write;
+ void *external_socket_data; /* used for external socket infra */
+ char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN
+ + 1]; /* should be used with is_tcp_auth_md5
+ flag */
+ bool is_tcp_auth_md5; /* flag to distinguish between rfc 2385 (md5) and
+ rfc 5925 (tcp-ao) */
+
+} pcep_socket_comm_session;
+
+
+/* Need to document that when the msg_rcv_handler is called, the data needs
+ * to be handled in the same function call, else it may be overwritten by
+ * the next read from this socket */
+
+
+/* Initialize the Socket Comm infrastructure, with either an internal pthread
+ * or with an external infrastructure.
+ * If an internal pthread infrastructure is to be used, then it is not necessary
+ * to explicitly call initialize_socket_comm_loop() as it will be called
+ * internally when a socket comm session is initialized. */
+
+/* Initialize the Socket Comm infrastructure with an internal pthread */
+bool initialize_socket_comm_loop(void);
+/* Initialize the Socket Comm infrastructure with an external infrastructure.
+ * Notice: If the thread_create_func is set, then both the socket_read_cb
+ * and the socket_write_cb SHOULD be NULL. */
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func);
+
+/* The msg_rcv_handler and msg_ready_handler are mutually exclusive, and only
+ * one can be set (as explained above), else NULL will be returned. */
+pcep_socket_comm_session *
+socket_comm_session_initialize(message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier,
+ struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dst_ip,
+ short dst_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data);
+
+bool socket_comm_session_teardown(
+ pcep_socket_comm_session *socket_comm_session);
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session);
+
+/* Immediately close the TCP connection, irregardless if there are pending
+ * messages to be sent. */
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session);
+
+/* Sets a flag to close the TCP connection either after all the pending messages
+ * are written, or if there are no pending messages, the next time the socket is
+ * checked to be writeable. */
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session);
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool free_after_send);
+
+/* If an external Socket infra like FRR is used, then these functions will
+ * be called when a socket is ready to read/write in the external infra.
+ * Implemented in pcep_socket_comm_loop.c */
+int pceplib_external_socket_read(int fd, void *payload);
+int pceplib_external_socket_write(int fd, void *payload);
+
+/* the socket comm loop is started internally by
+ * socket_comm_session_initialize()
+ * but needs to be explicitly stopped with this call. */
+bool destroy_socket_comm_loop(void);
+
+int socket_fd_node_compare(void *list_entry, void *new_entry);
+
+#endif /* INCLUDE_PCEPSOCKETCOMM_H_ */
diff --git a/pceplib/pcep_socket_comm_internals.h b/pceplib/pcep_socket_comm_internals.h
new file mode 100644
index 0000000000..4445a14fac
--- /dev/null
+++ b/pceplib/pcep_socket_comm_internals.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef SRC_PCEPSOCKETCOMMINTERNALS_H_
+#define SRC_PCEPSOCKETCOMMINTERNALS_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_utils_ordered_list.h"
+#include "pcep_socket_comm.h"
+
+
+typedef struct pcep_socket_comm_handle_ {
+ bool active;
+ pthread_t socket_comm_thread;
+ pthread_mutex_t socket_comm_mutex;
+ fd_set read_master_set;
+ fd_set write_master_set;
+ fd_set except_master_set;
+ /* ordered_list of socket_descriptors to read from */
+ ordered_list_handle *read_list;
+ /* ordered_list of socket_descriptors to write to */
+ ordered_list_handle *write_list;
+ ordered_list_handle *session_list;
+ int num_active_sessions;
+ void *external_infra_data;
+ ext_socket_write socket_write_func;
+ ext_socket_read socket_read_func;
+
+} pcep_socket_comm_handle;
+
+
+typedef struct pcep_socket_comm_queued_message_ {
+ const char *encoded_message;
+ int msg_length;
+ bool free_after_send;
+
+} pcep_socket_comm_queued_message;
+
+
+/* Functions implemented in pcep_socket_comm_loop.c */
+void *socket_comm_loop(void *data);
+bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session);
+bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session);
+
+#endif /* SRC_PCEPSOCKETCOMMINTERNALS_H_ */
diff --git a/pceplib/pcep_socket_comm_loop.c b/pceplib/pcep_socket_comm_loop.c
new file mode 100644
index 0000000000..8346c93025
--- /dev/null
+++ b/pceplib/pcep_socket_comm_loop.c
@@ -0,0 +1,486 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_loop.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+void write_message(int socket_fd, const char *message, unsigned int msg_length);
+unsigned int read_message(int socket_fd, char *received_message,
+ unsigned int max_message_size);
+int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle);
+void handle_writes(pcep_socket_comm_handle *socket_comm_handle);
+void handle_excepts(pcep_socket_comm_handle *socket_comm_handle);
+
+bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle == NULL) {
+ return false;
+ }
+
+ return (ordered_list_find(socket_comm_handle->session_list,
+ socket_comm_session)
+ != NULL);
+}
+
+
+bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle,
+ pcep_socket_comm_session *socket_comm_session)
+{
+ if (socket_comm_handle == NULL) {
+ return false;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ bool exists =
+ comm_session_exists(socket_comm_handle, socket_comm_session);
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return exists;
+}
+
+
+void write_message(int socket_fd, const char *message, unsigned int msg_length)
+{
+ ssize_t bytes_sent = 0;
+ unsigned int total_bytes_sent = 0;
+
+ while ((uint32_t)bytes_sent < msg_length) {
+ bytes_sent = write(socket_fd, message + total_bytes_sent,
+ msg_length);
+
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm writing on socket fd [%d] msg_lenth [%u] bytes sent [%d]",
+ __func__, time(NULL), pthread_self(), socket_fd,
+ msg_length, bytes_sent);
+
+ if (bytes_sent < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ pcep_log(LOG_WARNING, "%s: send() failure",
+ __func__);
+
+ return;
+ }
+ } else {
+ total_bytes_sent += bytes_sent;
+ }
+ }
+}
+
+
+unsigned int read_message(int socket_fd, char *received_message,
+ unsigned int max_message_size)
+{
+ /* TODO what if bytes_read == max_message_size? there could be more to
+ * read */
+ unsigned int bytes_read =
+ read(socket_fd, received_message, max_message_size);
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] socket_comm read message bytes_read [%u] on socket fd [%d]",
+ __func__, time(NULL), pthread_self(), bytes_read, socket_fd);
+
+ return bytes_read;
+}
+
+
+int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle)
+{
+ int max_fd = 0;
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+
+ FD_ZERO(&socket_comm_handle->except_master_set);
+ FD_ZERO(&socket_comm_handle->read_master_set);
+ ordered_list_node *node = socket_comm_handle->read_list->head;
+ pcep_socket_comm_session *comm_session;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ if (comm_session->socket_fd > max_fd) {
+ max_fd = comm_session->socket_fd;
+ }
+
+ /*pcep_log(LOG_DEBUG, ld] socket_comm::build_fdSets set
+ ready_toRead
+ [%d]", __func__, time(NULL), comm_session->socket_fd);*/
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->read_master_set);
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->except_master_set);
+ node = node->next_node;
+ }
+
+ FD_ZERO(&socket_comm_handle->write_master_set);
+ node = socket_comm_handle->write_list->head;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ if (comm_session->socket_fd > max_fd) {
+ max_fd = comm_session->socket_fd;
+ }
+
+ /*pcep_log(LOG_DEBUG, "%s: [%ld] socket_comm::build_fdSets set
+ ready_toWrite [%d]", __func__, time(NULL),
+ comm_session->socket_fd);*/
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->write_master_set);
+ FD_SET(comm_session->socket_fd,
+ &socket_comm_handle->except_master_set);
+ node = node->next_node;
+ }
+
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return max_fd + 1;
+}
+
+
+void handle_reads(pcep_socket_comm_handle *socket_comm_handle)
+{
+
+ /*
+ * iterate all the socket_fd's in the read_list. it may be that not
+ * all of them have something to read. dont remove the socket_fd
+ * from the read_list since messages could come at any time.
+ */
+
+ /* Notice: Only locking the mutex when accessing the read_list,
+ * since the read callbacks may end up calling back into the socket
+ * comm module to write messages which could be a deadlock. */
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ ordered_list_node *node = socket_comm_handle->read_list->head;
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ while (node != NULL) {
+ pcep_socket_comm_session *comm_session =
+ (pcep_socket_comm_session *)node->data;
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ node = node->next_node;
+ if (!comm_session_exists(socket_comm_handle, comm_session)) {
+ /* This comm_session has been deleted, move on to the
+ * next one */
+ pthread_mutex_unlock(
+ &(socket_comm_handle->socket_comm_mutex));
+ continue;
+ }
+
+ int is_set = FD_ISSET(comm_session->socket_fd,
+ &(socket_comm_handle->read_master_set));
+ /* Upon read failure, the comm_session might be free'd, so we
+ * cant store the received_bytes in the comm_session, until we
+ * know the read was successful. */
+ int received_bytes = 0;
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ if (is_set) {
+ FD_CLR(comm_session->socket_fd,
+ &(socket_comm_handle->read_master_set));
+
+ /* either read the message locally, or call the
+ * message_ready_handler to read it */
+ if (comm_session->message_handler != NULL) {
+ received_bytes = read_message(
+ comm_session->socket_fd,
+ comm_session->received_message,
+ MAX_RECVD_MSG_SIZE);
+ if (received_bytes > 0) {
+ /* Send the received message to the
+ * handler */
+ comm_session->received_bytes =
+ received_bytes;
+ comm_session->message_handler(
+ comm_session->session_data,
+ comm_session->received_message,
+ comm_session->received_bytes);
+ }
+ } else {
+ /* Tell the handler a message is ready to be
+ * read. The comm_session may be destroyed in
+ * this call, if
+ * there is an error reading or if the socket is
+ * closed. */
+ received_bytes =
+ comm_session
+ ->message_ready_to_read_handler(
+ comm_session
+ ->session_data,
+ comm_session
+ ->socket_fd);
+ }
+
+ /* handle the read results */
+ if (received_bytes == 0) {
+ if (comm_session_exists_locking(
+ socket_comm_handle, comm_session)) {
+ comm_session->received_bytes = 0;
+ /* the socket was closed */
+ /* TODO should we define a socket except
+ * enum? or will the only time we call
+ * this is when the socket is closed??
+ */
+ if (comm_session->conn_except_notifier
+ != NULL) {
+ comm_session->conn_except_notifier(
+ comm_session
+ ->session_data,
+ comm_session
+ ->socket_fd);
+ }
+
+ /* stop reading from the socket if its
+ * closed */
+ pthread_mutex_lock(
+ &(socket_comm_handle
+ ->socket_comm_mutex));
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->read_list,
+ comm_session);
+ pthread_mutex_unlock(
+ &(socket_comm_handle
+ ->socket_comm_mutex));
+ }
+ } else if (received_bytes < 0) {
+ /* TODO should we call conn_except_notifier()
+ * here ? */
+ pcep_log(
+ LOG_WARNING,
+ "%s: Error on socket fd [%d] : errno [%d][%s]",
+ __func__, comm_session->socket_fd,
+ errno, strerror(errno));
+ } else {
+ comm_session->received_bytes = received_bytes;
+ }
+ }
+ }
+}
+
+
+void handle_writes(pcep_socket_comm_handle *socket_comm_handle)
+{
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+
+ /*
+ * iterate all the socket_fd's in the write_list. it may be that not
+ * all of them are ready to be written to. only remove the socket_fd
+ * from the list if it is ready to be written to.
+ */
+
+ ordered_list_node *node = socket_comm_handle->write_list->head;
+ pcep_socket_comm_session *comm_session;
+ bool msg_written;
+ while (node != NULL) {
+ comm_session = (pcep_socket_comm_session *)node->data;
+ node = node->next_node;
+ msg_written = false;
+
+ if (!comm_session_exists(socket_comm_handle, comm_session)) {
+ /* This comm_session has been deleted, move on to the
+ * next one */
+ continue;
+ }
+
+ if (FD_ISSET(comm_session->socket_fd,
+ &(socket_comm_handle->write_master_set))) {
+ /* only remove the entry from the list, if it is written
+ * to */
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->write_list, comm_session);
+ FD_CLR(comm_session->socket_fd,
+ &(socket_comm_handle->write_master_set));
+
+ /* dequeue all the comm_session messages and send them
+ */
+ pcep_socket_comm_queued_message *queued_message =
+ queue_dequeue(comm_session->message_queue);
+ while (queued_message != NULL) {
+ msg_written = true;
+ write_message(comm_session->socket_fd,
+ queued_message->encoded_message,
+ queued_message->msg_length);
+ if (queued_message->free_after_send) {
+ pceplib_free(PCEPLIB_MESSAGES,
+ (void *)queued_message
+ ->encoded_message);
+ }
+ pceplib_free(PCEPLIB_MESSAGES, queued_message);
+ queued_message = queue_dequeue(
+ comm_session->message_queue);
+ }
+ }
+
+ /* check if the socket should be closed after writing */
+ if (comm_session->close_after_write == true) {
+ if (comm_session->message_queue->num_entries == 0) {
+ /* TODO check to make sure modifying the
+ * write_list while iterating it doesnt cause
+ * problems. */
+ pcep_log(
+ LOG_DEBUG,
+ "%s: handle_writes close() socket fd [%d]",
+ __func__, comm_session->socket_fd);
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->read_list,
+ comm_session);
+ ordered_list_remove_first_node_equals(
+ socket_comm_handle->write_list,
+ comm_session);
+ close(comm_session->socket_fd);
+ comm_session->socket_fd = -1;
+ }
+ }
+
+ if (comm_session->message_sent_handler != NULL
+ && msg_written == true) {
+ /* Unlocking to allow the message_sent_handler to
+ * make calls like destroy_socket_comm_session */
+ pthread_mutex_unlock(
+ &(socket_comm_handle->socket_comm_mutex));
+ comm_session->message_sent_handler(
+ comm_session->session_data,
+ comm_session->socket_fd);
+ pthread_mutex_lock(
+ &(socket_comm_handle->socket_comm_mutex));
+ }
+ }
+
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+}
+
+
+void handle_excepts(pcep_socket_comm_handle *socket_comm_handle)
+{
+ /* TODO finish this */
+ (void)socket_comm_handle;
+}
+
+
+/* pcep_socket_comm::initialize_socket_comm_loop() will create a thread and
+ * invoke this method */
+void *socket_comm_loop(void *data)
+{
+ if (data == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Cannot start socket_comm_loop with NULL pcep_socketcomm_handle",
+ __func__);
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting socket_comm_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)data;
+ struct timeval timer;
+ int max_fd;
+
+ while (socket_comm_handle->active) {
+ /* check the FD's every 1/4 sec, 250 milliseconds */
+ timer.tv_sec = 0;
+ timer.tv_usec = 250000;
+ max_fd = build_fd_sets(socket_comm_handle);
+
+ if (select(max_fd, &(socket_comm_handle->read_master_set),
+ &(socket_comm_handle->write_master_set),
+ &(socket_comm_handle->except_master_set), &timer)
+ < 0) {
+ /* TODO handle the error */
+ pcep_log(
+ LOG_WARNING,
+ "%s: ERROR socket_comm_loop on select : errno [%d][%s]",
+ __func__, errno, strerror(errno));
+ }
+
+ handle_reads(socket_comm_handle);
+ handle_writes(socket_comm_handle);
+ handle_excepts(socket_comm_handle);
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished socket_comm_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
+
+int pceplib_external_socket_read(int fd, void *payload)
+{
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)payload;
+ if (socket_comm_handle == NULL) {
+ return -1;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ FD_SET(fd, &(socket_comm_handle->read_master_set));
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ handle_reads(socket_comm_handle);
+
+ /* Get the socket_comm_session */
+ pcep_socket_comm_session find_session = {.socket_fd = fd};
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ ordered_list_node *node =
+ ordered_list_find(socket_comm_handle->read_list, &find_session);
+
+ /* read again */
+ if (node != NULL) {
+ socket_comm_handle->socket_read_func(
+ socket_comm_handle->external_infra_data,
+ &((pcep_socket_comm_session *)node)
+ ->external_socket_data,
+ fd, socket_comm_handle);
+ }
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ return 0;
+}
+
+int pceplib_external_socket_write(int fd, void *payload)
+{
+ pcep_socket_comm_handle *socket_comm_handle =
+ (pcep_socket_comm_handle *)payload;
+ if (socket_comm_handle == NULL) {
+ return -1;
+ }
+
+ pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex));
+ FD_SET(fd, &(socket_comm_handle->write_master_set));
+ pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex));
+
+ handle_writes(socket_comm_handle);
+
+ /* TODO do we need to cancel this FD from writing?? */
+
+ return 0;
+}
diff --git a/pceplib/pcep_socket_comm_loop.h b/pceplib/pcep_socket_comm_loop.h
new file mode 100644
index 0000000000..3ca2c037fa
--- /dev/null
+++ b/pceplib/pcep_socket_comm_loop.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEPSOCKETCOMMLOOP_H_
+#define PCEPSOCKETCOMMLOOP_H_
+
+void handle_reads(pcep_socket_comm_handle *socket_comm_handle);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/pcep_socket_comm_mock.c b/pceplib/pcep_socket_comm_mock.c
new file mode 100644
index 0000000000..069d0cf998
--- /dev/null
+++ b/pceplib/pcep_socket_comm_mock.c
@@ -0,0 +1,363 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * This module is built into a separate library, and is used by several
+ * other modules for unit testing, so that real sockets dont have to be
+ * created.
+ */
+
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_mock.h"
+#include "pcep_utils_queue.h"
+
+/* reset_mock_socket_comm_info() should be used before each test */
+mock_socket_comm_info mock_socket_metadata;
+
+void setup_mock_socket_comm_info(void)
+{
+ mock_socket_metadata.socket_comm_session_initialize_times_called = 0;
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called =
+ 0;
+ mock_socket_metadata.socket_comm_session_teardown_times_called = 0;
+ mock_socket_metadata.socket_comm_session_connect_tcp_times_called = 0;
+ mock_socket_metadata.socket_comm_session_send_message_times_called = 0;
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called = 0;
+ mock_socket_metadata.socket_comm_session_close_tcp_times_called = 0;
+ mock_socket_metadata.destroy_socket_comm_loop_times_called = 0;
+ mock_socket_metadata.send_message_save_message = false;
+ mock_socket_metadata.sent_message_list = dll_initialize();
+}
+
+void teardown_mock_socket_comm_info(void)
+{
+ dll_destroy(mock_socket_metadata.sent_message_list);
+}
+
+void reset_mock_socket_comm_info(void)
+{
+ teardown_mock_socket_comm_info();
+ setup_mock_socket_comm_info();
+}
+
+mock_socket_comm_info *get_mock_socket_comm_info(void)
+{
+ return &mock_socket_metadata;
+}
+
+void verify_socket_comm_times_called(int initialized, int teardown, int connect,
+ int send_message,
+ int close_tcp_after_write, int close_tcp,
+ int destroy)
+{
+ CU_ASSERT_EQUAL(initialized,
+ mock_socket_metadata
+ .socket_comm_session_initialize_times_called);
+ CU_ASSERT_EQUAL(
+ teardown,
+ mock_socket_metadata.socket_comm_session_teardown_times_called);
+ CU_ASSERT_EQUAL(connect,
+ mock_socket_metadata
+ .socket_comm_session_connect_tcp_times_called);
+ CU_ASSERT_EQUAL(send_message,
+ mock_socket_metadata
+ .socket_comm_session_send_message_times_called);
+ CU_ASSERT_EQUAL(
+ close_tcp_after_write,
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called);
+ CU_ASSERT_EQUAL(close_tcp,
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_times_called);
+ CU_ASSERT_EQUAL(
+ destroy,
+ mock_socket_metadata.destroy_socket_comm_loop_times_called);
+}
+
+
+/*
+ * Mock the socket_comm functions used by session_logic for Unit Testing
+ */
+
+bool initialize_socket_comm_external_infra(
+ void *external_infra_data, ext_socket_read socket_read_cb,
+ ext_socket_write socket_write_cb,
+ ext_socket_pthread_create_callback thread_create_func)
+{
+ (void)external_infra_data;
+ (void)socket_read_cb;
+ (void)socket_write_cb;
+ (void)thread_create_func;
+
+ mock_socket_metadata
+ .socket_comm_initialize_external_infra_times_called++;
+
+ return true;
+}
+
+bool destroy_socket_comm_loop()
+{
+ mock_socket_metadata.destroy_socket_comm_loop_times_called++;
+
+ return false;
+}
+
+pcep_socket_comm_session *
+socket_comm_session_initialize(message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier,
+ struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = false;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dst_port);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr =
+ dst_ip->s_addr;
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *dst_ip,
+ short dst_port, uint32_t connect_timeout_millis,
+ const char *tcp_authentication_str, bool is_tcp_auth_md5,
+ void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = true;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dst_port);
+ memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr,
+ dst_ip, sizeof(struct in6_addr));
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in_addr *src_ip,
+ short src_port, struct in_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = false;
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port =
+ htons(src_port);
+ comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr =
+ ((src_ip == NULL) ? INADDR_ANY : src_ip->s_addr);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
+ htons(dst_port);
+ comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr =
+ dst_ip->s_addr;
+
+ return comm_session;
+}
+
+pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
+ message_received_handler msg_rcv_handler,
+ message_ready_to_read_handler msg_ready_handler,
+ message_sent_notifier msg_sent_notifier,
+ connection_except_notifier notifier, struct in6_addr *src_ip,
+ short src_port, struct in6_addr *dst_ip, short dst_port,
+ uint32_t connect_timeout_millis, const char *tcp_authentication_str,
+ bool is_tcp_auth_md5, void *session_data)
+{
+ (void)msg_sent_notifier;
+ (void)tcp_authentication_str;
+ (void)is_tcp_auth_md5;
+
+ mock_socket_metadata.socket_comm_session_initialize_src_times_called++;
+
+ pcep_socket_comm_session *comm_session =
+ malloc(sizeof(pcep_socket_comm_session));
+ memset(comm_session, 0, sizeof(pcep_socket_comm_session));
+
+ comm_session->message_handler = msg_rcv_handler;
+ comm_session->message_ready_to_read_handler = msg_ready_handler;
+ comm_session->conn_except_notifier = notifier;
+ comm_session->message_queue = queue_initialize();
+ comm_session->session_data = session_data;
+ comm_session->close_after_write = false;
+ comm_session->connect_timeout_millis = connect_timeout_millis;
+ comm_session->is_ipv6 = true;
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port =
+ htons(src_port);
+ if (src_ip == NULL) {
+ comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_addr =
+ in6addr_any;
+ } else {
+ memcpy(&comm_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ src_ip, sizeof(struct in6_addr));
+ }
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6;
+ comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
+ htons(dst_port);
+ memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr,
+ dst_ip, sizeof(struct in6_addr));
+
+ return comm_session;
+}
+
+bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session)
+{
+ mock_socket_metadata.socket_comm_session_teardown_times_called++;
+
+ if (socket_comm_session != NULL) {
+ queue_destroy(socket_comm_session->message_queue);
+ free(socket_comm_session);
+ }
+
+ return true;
+}
+
+
+bool socket_comm_session_connect_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata.socket_comm_session_connect_tcp_times_called++;
+
+ return true;
+}
+
+
+void socket_comm_session_send_message(
+ pcep_socket_comm_session *socket_comm_session,
+ const char *encoded_message, unsigned int msg_length,
+ bool delete_after_send)
+{
+ (void)socket_comm_session;
+ (void)msg_length;
+
+ mock_socket_metadata.socket_comm_session_send_message_times_called++;
+
+ if (mock_socket_metadata.send_message_save_message == true) {
+ /* the caller/test case is responsible for freeing the message
+ */
+ dll_append(mock_socket_metadata.sent_message_list,
+ (char *)encoded_message);
+ } else {
+ if (delete_after_send == true) {
+ free((void *)encoded_message);
+ }
+ }
+
+ return;
+}
+
+
+bool socket_comm_session_close_tcp_after_write(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata
+ .socket_comm_session_close_tcp_after_write_times_called++;
+
+ return true;
+}
+
+
+bool socket_comm_session_close_tcp(
+ pcep_socket_comm_session *socket_comm_session)
+{
+ (void)socket_comm_session;
+
+ mock_socket_metadata.socket_comm_session_close_tcp_times_called++;
+
+ return true;
+}
diff --git a/pceplib/pcep_socket_comm_mock.h b/pceplib/pcep_socket_comm_mock.h
new file mode 100644
index 0000000000..ca0e38b803
--- /dev/null
+++ b/pceplib/pcep_socket_comm_mock.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+/*
+ * This module is built into a separate library, and is used by several
+ * other modules for unit testing, so that real sockets dont have to be
+ * created.
+ */
+
+#ifndef PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_
+#define PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_
+
+#include <stdbool.h>
+
+#include "pcep_utils_double_linked_list.h"
+
+typedef struct mock_socket_comm_info_ {
+ int socket_comm_initialize_external_infra_times_called;
+ int socket_comm_session_initialize_times_called;
+ int socket_comm_session_initialize_src_times_called;
+ int socket_comm_session_teardown_times_called;
+ int socket_comm_session_connect_tcp_times_called;
+ int socket_comm_session_send_message_times_called;
+ int socket_comm_session_close_tcp_after_write_times_called;
+ int socket_comm_session_close_tcp_times_called;
+ int destroy_socket_comm_loop_times_called;
+
+ /* TODO later if necessary, we can add return values for
+ * those functions that return something */
+
+ /* Used to access messages sent with socket_comm_session_send_message()
+ */
+ bool send_message_save_message;
+ double_linked_list *sent_message_list;
+
+} mock_socket_comm_info;
+
+void setup_mock_socket_comm_info(void);
+void teardown_mock_socket_comm_info(void);
+void reset_mock_socket_comm_info(void);
+bool destroy_socket_comm_loop(void);
+
+mock_socket_comm_info *get_mock_socket_comm_info(void);
+void verify_socket_comm_times_called(int initialized, int teardown, int connect,
+ int send_message, int close_after_write,
+ int close, int destroy);
+
+#endif /* PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_ */
diff --git a/pceplib/pcep_timer_internals.h b/pceplib/pcep_timer_internals.h
new file mode 100644
index 0000000000..8221c78baa
--- /dev/null
+++ b/pceplib/pcep_timer_internals.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEPTIMERINTERNALS_H_
+#define PCEPTIMERINTERNALS_H_
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include "pcep_utils_ordered_list.h"
+
+/* Function pointer to be called when timers expire.
+ * Parameters:
+ * void *data - passed into create_timer
+ * int timer_id - the timer_id returned by create_timer
+ */
+typedef void (*timer_expire_handler)(void *, int);
+
+/* Function pointer when an external timer infrastructure is used */
+typedef void (*ext_timer_create)(void *infra_data, void **timer, int seconds,
+ void *data);
+typedef void (*ext_timer_cancel)(void **timer);
+typedef int (*ext_pthread_create_callback)(pthread_t *pthread_id,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *),
+ void *data, const char *thread_name);
+
+typedef struct pcep_timer_ {
+ time_t expire_time;
+ uint16_t sleep_seconds;
+ int timer_id;
+ void *data;
+ void *external_timer;
+
+} pcep_timer;
+
+typedef struct pcep_timers_context_ {
+ ordered_list_handle *timer_list;
+ bool active;
+ timer_expire_handler expire_handler;
+ pthread_t event_loop_thread;
+ pthread_mutex_t timer_list_lock;
+ void *external_timer_infra_data;
+ ext_timer_create timer_create_func;
+ ext_timer_cancel timer_cancel_func;
+
+} pcep_timers_context;
+
+/* functions implemented in pcep_timers_loop.c */
+void *event_loop(void *context);
+
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c
new file mode 100644
index 0000000000..e9d9d4b21d
--- /dev/null
+++ b/pceplib/pcep_timers.c
@@ -0,0 +1,482 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of public API timer functions.
+ */
+
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "pcep_timers.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+
+static pcep_timers_context *timers_context_ = NULL;
+static int timer_id_ = 0;
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * for ordered list insertion. */
+int timer_list_node_compare(void *list_entry, void *new_entry)
+{
+ /* return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry */
+ return ((pcep_timer *)new_entry)->expire_time
+ - ((pcep_timer *)list_entry)->expire_time;
+}
+
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its timer_id. */
+int timer_list_node_timer_id_compare(void *list_entry, void *new_entry)
+{
+ return ((pcep_timer *)new_entry)->timer_id
+ - ((pcep_timer *)list_entry)->timer_id;
+}
+
+/* simple compare method callback used by pcep_utils_ordered_list
+ * ordered_list_remove_first_node_equals2 to remove a timer based on
+ * its address. */
+int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry)
+{
+ return ((char *)new_entry - (char *)list_entry);
+}
+
+/* internal util method */
+static pcep_timers_context *create_timers_context_()
+{
+ if (timers_context_ == NULL) {
+ timers_context_ = pceplib_malloc(PCEPLIB_INFRA,
+ sizeof(pcep_timers_context));
+ memset(timers_context_, 0, sizeof(pcep_timers_context));
+ timers_context_->active = false;
+ }
+
+ return timers_context_;
+}
+
+
+/* Internal util function */
+static bool initialize_timers_common(timer_expire_handler expire_handler)
+{
+ if (expire_handler == NULL) {
+ /* Cannot have a NULL handler function */
+ return false;
+ }
+
+ timers_context_ = create_timers_context_();
+
+ if (timers_context_->active == true) {
+ /* already initialized */
+ return false;
+ }
+
+ timers_context_->active = true;
+ timers_context_->timer_list =
+ ordered_list_initialize(timer_list_node_compare);
+ timers_context_->expire_handler = expire_handler;
+
+ if (pthread_mutex_init(&(timers_context_->timer_list_lock), NULL)
+ != 0) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the mutex",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers(timer_expire_handler expire_handler)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool initialize_timers_external_infra(
+ timer_expire_handler expire_handler, void *external_timer_infra_data,
+ ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func,
+ ext_pthread_create_callback thread_create_func)
+{
+ if (initialize_timers_common(expire_handler) == false) {
+ return false;
+ }
+
+ if (thread_create_func != NULL) {
+ if (thread_create_func(&(timers_context_->event_loop_thread),
+ NULL, event_loop, timers_context_,
+ "pceplib_timers")) {
+ pcep_log(
+ LOG_ERR,
+ "%s: Cannot initialize external timers thread.",
+ __func__);
+ return false;
+ }
+ } else {
+ if (pthread_create(&(timers_context_->event_loop_thread), NULL,
+ event_loop, timers_context_)) {
+ pcep_log(
+ LOG_ERR,
+ "%s: ERROR initializing timers, cannot initialize the thread",
+ __func__);
+ return false;
+ }
+ }
+
+ timers_context_->external_timer_infra_data = external_timer_infra_data;
+ timers_context_->timer_create_func = timer_create_func;
+ timers_context_->timer_cancel_func = timer_cancel_func;
+
+ return true;
+}
+
+/*
+ * This function is only used to tear_down the timer data.
+ * Only the timer data is deleted, not the list itself,
+ * which is deleted by ordered_list_destroy().
+ */
+void free_all_timers(pcep_timers_context *timers_context)
+{
+ pthread_mutex_lock(&timers_context->timer_list_lock);
+
+ ordered_list_node *timer_node = timers_context->timer_list->head;
+
+ while (timer_node != NULL) {
+ if (timer_node->data != NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_node->data);
+ }
+ timer_node = timer_node->next_node;
+ }
+
+ pthread_mutex_unlock(&timers_context->timer_list_lock);
+}
+
+
+bool teardown_timers()
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not initialized",
+ __func__);
+ return false;
+ }
+
+ if (timers_context_->active == false) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, but they are not active",
+ __func__);
+ return false;
+ }
+
+ timers_context_->active = false;
+ if (timers_context_->event_loop_thread != 0) {
+ /* TODO this does not build
+ * Instead of calling pthread_join() which could block if the
+ thread
+ * is blocked, try joining for at most 1 second.
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ int retval =
+ pthread_timedjoin_np(timers_context_->event_loop_thread, NULL,
+ &ts); if (retval != 0)
+ {
+ pcep_log(LOG_WARNING, "%s: thread did not stop after 1
+ second waiting on it.", __func__);
+ }
+ */
+ pthread_join(timers_context_->event_loop_thread, NULL);
+ }
+
+ free_all_timers(timers_context_);
+ ordered_list_destroy(timers_context_->timer_list);
+
+ if (pthread_mutex_destroy(&(timers_context_->timer_list_lock)) != 0) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to teardown the timers, cannot destroy the mutex",
+ __func__);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timers_context_);
+ timers_context_ = NULL;
+
+ return true;
+}
+
+
+int get_next_timer_id()
+{
+ if (timer_id_ == INT_MAX) {
+ timer_id_ = 0;
+ }
+
+ return timer_id_++;
+}
+
+int create_timer(uint16_t sleep_seconds, void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer: the timers have not been initialized",
+ __func__);
+ return -1;
+ }
+
+ pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer));
+ memset(timer, 0, sizeof(pcep_timer));
+ timer->data = data;
+ timer->sleep_seconds = sleep_seconds;
+ timer->expire_time = time(NULL) + sleep_seconds;
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ timer->timer_id = get_next_timer_id();
+
+ /* implemented in pcep_utils_ordered_list.c */
+ if (ordered_list_add_node(timers_context_->timer_list, timer) == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to create a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return -1;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer->external_timer, sleep_seconds, timer);
+ }
+
+ return timer->timer_id;
+}
+
+
+bool cancel_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer: the timers have not been initialized",
+ __func__);
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ pcep_timer *timer_toRemove = ordered_list_remove_first_node_equals2(
+ timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_toRemove == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to cancel a timer [%d] that does not exist",
+ __func__, timer_id);
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ timers_context_->timer_cancel_func(
+ &timer_toRemove->external_timer);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, timer_toRemove);
+
+ return true;
+}
+
+
+bool reset_timer(int timer_id)
+{
+ static pcep_timer compare_timer;
+
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer: the timers have not been initialized",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+
+ compare_timer.timer_id = timer_id;
+ ordered_list_node *timer_to_reset_node =
+ ordered_list_find2(timers_context_->timer_list, &compare_timer,
+ timer_list_node_timer_id_compare);
+ if (timer_to_reset_node == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer node that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ pcep_timer *timer_to_reset = timer_to_reset_node->data;
+ if (timer_to_reset == NULL) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(LOG_WARNING,
+ "%s: Trying to reset a timer that does not exist",
+ __func__);
+
+ return false;
+ }
+
+ /* First check if the timer to reset already has the same expire time,
+ * which means multiple reset_timer() calls were made on the same timer
+ * in the same second */
+ time_t expire_time = time(NULL) + timer_to_reset->sleep_seconds;
+ if (timer_to_reset->expire_time == expire_time) {
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ return true;
+ }
+
+ ordered_list_remove_node2(timers_context_->timer_list,
+ timer_to_reset_node);
+
+ timer_to_reset->expire_time = expire_time;
+ if (ordered_list_add_node(timers_context_->timer_list, timer_to_reset)
+ == NULL) {
+ pceplib_free(PCEPLIB_INFRA, timer_to_reset);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+ pcep_log(
+ LOG_WARNING,
+ "%s: Trying to reset a timer, cannot add the timer to the timer list",
+ __func__);
+
+ return false;
+ }
+
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ if (timers_context_->timer_cancel_func) {
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Reseting timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ timers_context_->timer_cancel_func(
+ &timer_to_reset->external_timer);
+ timer_to_reset->external_timer = NULL;
+ }
+
+ if (timers_context_->timer_create_func) {
+ timers_context_->timer_create_func(
+ timers_context_->external_timer_infra_data,
+ &timer_to_reset->external_timer,
+ timer_to_reset->sleep_seconds, timer_to_reset);
+ /* Keeping this log for now, since in older versions of FRR the
+ * timer cancellation was blocking. This allows us to see how
+ * long the it takes.*/
+ pcep_log(LOG_DEBUG, "%s: Reset timer [%d] with callback",
+ __func__, timer_to_reset->timer_id);
+ }
+
+ return true;
+}
+
+
+void pceplib_external_timer_expire_handler(void *data)
+{
+ if (timers_context_ == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but timers_context is not initialized",
+ __func__);
+ return;
+ }
+
+ if (timers_context_->expire_handler == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: External timer expired but expire_handler is not initialized",
+ __func__);
+ return;
+ }
+
+ if (data == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: External timer expired with NULL data", __func__);
+ return;
+ }
+
+ pcep_timer *timer = (pcep_timer *)data;
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ ordered_list_node *timer_node =
+ ordered_list_find2(timers_context_->timer_list, timer,
+ timer_list_node_timer_ptr_compare);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ /* Cannot continue if the timer does not exist */
+ if (timer_node == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist",
+ __func__, timer, timer->timer_id);
+ return;
+ }
+
+ timers_context_->expire_handler(timer->data, timer->timer_id);
+
+ pthread_mutex_lock(&timers_context_->timer_list_lock);
+ ordered_list_remove_node2(timers_context_->timer_list, timer_node);
+ pthread_mutex_unlock(&timers_context_->timer_list_lock);
+
+ pceplib_free(PCEPLIB_INFRA, timer);
+}
diff --git a/pceplib/pcep_timers.h b/pceplib/pcep_timers.h
new file mode 100644
index 0000000000..b2cc6ec546
--- /dev/null
+++ b/pceplib/pcep_timers.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Public API for pcep_timers
+ */
+
+#ifndef PCEPTIMERS_H_
+#define PCEPTIMERS_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "pcep_timer_internals.h"
+
+#define TIMER_ID_NOT_SET -1
+
+/*
+ * Initialize the timers module.
+ * The timer_expire_handler function pointer will be called each time a timer
+ * expires. Return true for successful initialization, false otherwise.
+ */
+bool initialize_timers(timer_expire_handler expire_handler);
+
+/*
+ * Initialize the timers module with an external back-end infrastructure, like
+ * FRR.
+ */
+bool initialize_timers_external_infra(
+ timer_expire_handler expire_handler, void *external_timer_infra_data,
+ ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func,
+ ext_pthread_create_callback thread_create_func);
+
+/*
+ * Teardown the timers module.
+ */
+bool teardown_timers(void);
+
+/*
+ * Create a new timer for "sleep_seconds" seconds.
+ * If the timer expires before being cancelled, the timer_expire_handler
+ * passed to initialize_timers() will be called with the pointer to "data".
+ * Returns a timer_id <= 0 that can be used to cancel_timer.
+ * Returns < 0 on error.
+ */
+int create_timer(uint16_t sleep_seconds, void *data);
+
+/*
+ * Cancel a timer created with create_timer().
+ * Returns true if the timer was found and cancelled, false otherwise.
+ */
+bool cancel_timer(int timer_id);
+
+/*
+ * Reset an previously created timer, maintaining the same timer_id.
+ * Returns true if the timer was found and reset, false otherwise.
+ */
+bool reset_timer(int timer_id);
+
+/*
+ * If an external timer infra like FRR is used, then this function
+ * will be called when the timers expire in the external infra.
+ */
+void pceplib_external_timer_expire_handler(void *data);
+
+int timer_list_node_compare(void *list_entry, void *new_entry);
+int timer_list_node_timer_id_compare(void *list_entry, void *new_entry);
+int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry);
+void free_all_timers(pcep_timers_context *timers_context);
+int get_next_timer_id(void);
+
+#endif /* PCEPTIMERS_H_ */
diff --git a/pceplib/pcep_timers_event_loop.c b/pceplib/pcep_timers_event_loop.c
new file mode 100644
index 0000000000..932a53eb2a
--- /dev/null
+++ b/pceplib/pcep_timers_event_loop.c
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/select.h>
+
+#include "pcep_timers_event_loop.h"
+#include "pcep_timer_internals.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* For each expired timer: remove the timer from the list, call the
+ * expire_handler, and free the timer. */
+void walk_and_process_timers(pcep_timers_context *timers_context)
+{
+ pthread_mutex_lock(&timers_context->timer_list_lock);
+
+ bool keep_walking = true;
+ ordered_list_node *timer_node = timers_context->timer_list->head;
+ time_t now = time(NULL);
+ pcep_timer *timer_data;
+
+ /* the timers are sorted by expire_time, so we will only
+ * remove the top node each time through the loop */
+ while (timer_node != NULL && keep_walking) {
+ timer_data = (pcep_timer *)timer_node->data;
+ if (timer_data->expire_time <= now) {
+ timer_node = timer_node->next_node;
+ ordered_list_remove_first_node(
+ timers_context->timer_list);
+ /* call the timer expired handler */
+ timers_context->expire_handler(timer_data->data,
+ timer_data->timer_id);
+ pceplib_free(PCEPLIB_INFRA, timer_data);
+ } else {
+ keep_walking = false;
+ }
+ }
+
+ pthread_mutex_unlock(&timers_context->timer_list_lock);
+}
+
+
+/* pcep_timers::initialize() will create a thread and invoke this method */
+void *event_loop(void *context)
+{
+ if (context == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: pcep_timers_event_loop cannot start event_loop with NULL data",
+ __func__);
+ return NULL;
+ }
+
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting timers_event_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ pcep_timers_context *timers_context = (pcep_timers_context *)context;
+ struct timeval timer;
+ int retval;
+
+ while (timers_context->active) {
+ /* check the timers every half second */
+ timer.tv_sec = 0;
+ timer.tv_usec = 500000;
+
+ do {
+ /* if the select() call gets interrupted, select() will
+ * set the remaining time in timer, so we need to call
+ * it again.
+ */
+ retval = select(0, NULL, NULL, NULL, &timer);
+ } while (retval != 0 && errno == EINTR);
+
+ walk_and_process_timers(timers_context);
+ }
+
+ pcep_log(LOG_WARNING, "%s: [%ld-%ld] Finished timers_event_loop thread",
+ __func__, time(NULL), pthread_self());
+
+ return NULL;
+}
diff --git a/pceplib/pcep_timers_event_loop.h b/pceplib/pcep_timers_event_loop.h
new file mode 100644
index 0000000000..c4a7264da3
--- /dev/null
+++ b/pceplib/pcep_timers_event_loop.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_EVENT_LOOP_H_
+#define PCEP_TIMERS_EVENT_LOOP_H_
+
+#include "pcep_timer_internals.h"
+
+void walk_and_process_timers(pcep_timers_context *timers_context);
+
+#endif
diff --git a/pceplib/pcep_utils_counters.c b/pceplib/pcep_utils_counters.c
new file mode 100644
index 0000000000..d8078f683f
--- /dev/null
+++ b/pceplib/pcep_utils_counters.c
@@ -0,0 +1,475 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Implementation of PCEP Counters.
+ */
+
+#include <zebra.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "pcep_utils_counters.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+struct counters_group *create_counters_group(const char *group_name,
+ uint16_t max_subgroups)
+{
+ if (group_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters group: group_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (max_subgroups > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters group: max_subgroups [%d] is larger than max the [%d].",
+ __func__, max_subgroups, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_group *group =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_group));
+ memset(group, 0, sizeof(struct counters_group));
+ group->subgroups =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup *)
+ * (max_subgroups + 1));
+ memset(group->subgroups, 0,
+ sizeof(struct counters_subgroup *) * (max_subgroups + 1));
+
+ strlcpy(group->counters_group_name, group_name,
+ sizeof(group->counters_group_name));
+ group->max_subgroups = max_subgroups;
+ group->start_time = time(NULL);
+
+ return group;
+}
+
+struct counters_subgroup *create_counters_subgroup(const char *subgroup_name,
+ uint16_t subgroup_id,
+ uint16_t max_counters)
+{
+ if (subgroup_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: subgroup_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (max_counters > MAX_COUNTERS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: max_counters [%d] is larger than the max [%d].",
+ __func__, max_counters, MAX_COUNTERS);
+ return NULL;
+ }
+
+ if (subgroup_id > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create counters subgroup: subgroup_id [%d] is larger than max the [%d].",
+ __func__, subgroup_id, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_subgroup *subgroup =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup));
+ memset(subgroup, 0, sizeof(struct counters_subgroup));
+ subgroup->counters = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(struct counter *) * (max_counters + 1));
+ memset(subgroup->counters, 0,
+ sizeof(struct counter *) * (max_counters + 1));
+
+ strlcpy(subgroup->counters_subgroup_name, subgroup_name,
+ sizeof(subgroup->counters_subgroup_name));
+ subgroup->subgroup_id = subgroup_id;
+ subgroup->max_counters = max_counters;
+
+ return subgroup;
+}
+
+struct counters_subgroup *
+clone_counters_subgroup(struct counters_subgroup *subgroup,
+ const char *subgroup_name, uint16_t subgroup_id)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: input counters_subgroup is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (subgroup_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: subgroup_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ if (subgroup_id > MAX_COUNTER_GROUPS) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot clone counters subgroup: subgroup_id [%d] is larger than max the [%d].",
+ __func__, subgroup_id, MAX_COUNTER_GROUPS);
+ return NULL;
+ }
+
+ struct counters_subgroup *cloned_subgroup = create_counters_subgroup(
+ subgroup_name, subgroup_id, subgroup->max_counters);
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ create_subgroup_counter(cloned_subgroup,
+ counter->counter_id,
+ counter->counter_name);
+ }
+ }
+
+ return cloned_subgroup;
+}
+
+bool add_counters_subgroup(struct counters_group *group,
+ struct counters_subgroup *subgroup)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup->subgroup_id >= group->max_subgroups) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot add counters subgroup: counters_subgroup id [%d] is larger than the group max_subgroups [%d].",
+ __func__, subgroup->subgroup_id, group->max_subgroups);
+ return false;
+ }
+
+ group->num_subgroups++;
+ group->subgroups[subgroup->subgroup_id] = subgroup;
+
+ return true;
+}
+
+bool create_subgroup_counter(struct counters_subgroup *subgroup,
+ uint32_t counter_id, const char *counter_name)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (counter_id >= subgroup->max_counters) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counter_id [%d] is larger than the subgroup max_counters [%d].",
+ __func__, counter_id, subgroup->max_counters);
+ return false;
+ }
+
+ if (counter_name == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot create subgroup counter: counter_name is NULL.",
+ __func__);
+ return NULL;
+ }
+
+ struct counter *counter =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counter));
+ memset(counter, 0, sizeof(struct counter));
+ counter->counter_id = counter_id;
+ strlcpy(counter->counter_name, counter_name,
+ sizeof(counter->counter_name));
+
+ subgroup->num_counters++;
+ subgroup->counters[counter->counter_id] = counter;
+
+ return true;
+}
+
+bool delete_counters_group(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot delete group counters: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ delete_counters_subgroup(subgroup);
+ }
+ }
+
+ pceplib_free(PCEPLIB_INFRA, group->subgroups);
+ pceplib_free(PCEPLIB_INFRA, group);
+
+ return true;
+}
+
+bool delete_counters_subgroup(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL || subgroup->counters == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot delete subgroup counters: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ pceplib_free(PCEPLIB_INFRA, counter);
+ }
+ }
+
+ pceplib_free(PCEPLIB_INFRA, subgroup->counters);
+ pceplib_free(PCEPLIB_INFRA, subgroup);
+
+ return true;
+}
+
+bool reset_group_counters(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot reset group counters: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ reset_subgroup_counters(subgroup);
+ }
+ }
+
+ group->start_time = time(NULL);
+
+ return true;
+}
+
+bool reset_subgroup_counters(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot reset subgroup counters: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ counter->counter_value = 0;
+ }
+ }
+
+ return true;
+}
+
+bool increment_counter(struct counters_group *group, uint16_t subgroup_id,
+ uint16_t counter_id)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (subgroup_id >= group->max_subgroups) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Cannot increment counter: subgroup_id [%d] is larger than the group max_subgroups [%d].",
+ __func__, subgroup_id, group->max_subgroups);
+ return false;
+ }
+
+ struct counters_subgroup *subgroup = group->subgroups[subgroup_id];
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_subgroup in counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ return increment_subgroup_counter(subgroup, counter_id);
+}
+
+bool increment_subgroup_counter(struct counters_subgroup *subgroup,
+ uint16_t counter_id)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ if (counter_id >= subgroup->max_counters) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: Cannot increment counter: counter_id [%d] is larger than the subgroup max_counters [%d].",
+ __func__, counter_id, subgroup->max_counters);
+ return false;
+ }
+
+ if (subgroup->counters[counter_id] == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot increment counter: No counter exists for counter_id [%d].",
+ __func__, counter_id);
+ return false;
+ }
+
+ subgroup->counters[counter_id]->counter_value++;
+
+ return true;
+}
+
+bool dump_counters_group_to_log(struct counters_group *group)
+{
+ if (group == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot dump group counters to log: counters_group is NULL.",
+ __func__);
+ return false;
+ }
+
+ time_t now = time(NULL);
+ pcep_log(
+ LOG_INFO,
+ "%s: PCEP Counters group:\n %s \n Sub-Groups [%d] \n Active for [%d seconds]",
+ __func__, group->counters_group_name, group->num_subgroups,
+ (now - group->start_time));
+
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ dump_counters_subgroup_to_log(subgroup);
+ }
+ }
+
+ return true;
+}
+
+bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Cannot dump subgroup counters to log: counters_subgroup is NULL.",
+ __func__);
+ return false;
+ }
+
+ pcep_log(LOG_INFO,
+ "%s: \tPCEP Counters sub-group [%s] with [%d] counters",
+ __func__, subgroup->counters_subgroup_name,
+ subgroup->num_counters);
+
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ pcep_log(LOG_INFO, "%s: \t\t%s %d", __func__,
+ counter->counter_name, counter->counter_value);
+ }
+ }
+
+ return true;
+}
+
+struct counters_subgroup *find_subgroup(const struct counters_group *group,
+ uint16_t subgroup_id)
+{
+ int i = 0;
+ for (; i <= group->max_subgroups; i++) {
+ struct counters_subgroup *subgroup = group->subgroups[i];
+ if (subgroup != NULL) {
+ if (subgroup->subgroup_id == subgroup_id) {
+ return subgroup;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+uint32_t subgroup_counters_total(struct counters_subgroup *subgroup)
+{
+ if (subgroup == NULL) {
+ return 0;
+ }
+ uint32_t counter_total = 0;
+ int i = 0;
+ for (; i <= subgroup->max_counters; i++) {
+ struct counter *counter = subgroup->counters[i];
+ if (counter != NULL) {
+ counter_total += counter->counter_value;
+ }
+ }
+
+ return counter_total;
+}
diff --git a/pceplib/pcep_utils_counters.h b/pceplib/pcep_utils_counters.h
new file mode 100644
index 0000000000..240e9758b7
--- /dev/null
+++ b/pceplib/pcep_utils_counters.h
@@ -0,0 +1,232 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Definitions of PCEP Counters.
+ */
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Example Counter group with sub-groups and counters
+ *
+ * pcep_counters {
+ * counters_group_rx {
+ * message_open;
+ * message_keepalive;
+ * message_pcreq;
+ * }
+ * counters_group_tx {
+ * message_open;
+ * message_keepalive;
+ * message_pcreq;
+ * }
+ * counters_group_events {
+ * pcc_connect;
+ * pce_connect;
+ * pcc_disconnect;
+ * pce_disconnect;
+ * }
+ * }
+ *
+ * To create the above structure of groups, sub-groups, and counters, do the
+ * following:
+ *
+ * struct counters_subgroup *rx_subgroup = create_counters_subgroup("rx
+ * counters", 3); struct counters_subgroup *tx_subgroup =
+ * create_counters_subgroup("tx counters", 3); struct counters_subgroup
+ * *events_subgroup = create_counters_subgroup("events counters", 4);
+ *
+ * Use message_id: PCEP_TYPE_OPEN=1
+ * create_subgroup_counter(rx_subgroup, 1, "Message Open");
+ * create_subgroup_counter(rx_subgroup, 2, "Message KeepAlive");
+ * create_subgroup_counter(rx_subgroup, 3, "Message PcReq");
+ *
+ * create_subgroup_counter(tx_subgroup, 1, "Message Open");
+ * create_subgroup_counter(tx_subgroup, 2, "Message KeepAlive");
+ * create_subgroup_counter(tx_subgroup, 3, "Message PcReq");
+ *
+ * create_subgroup_counter(events_subgroup, 1, "PCC Connect");
+ * create_subgroup_counter(events_subgroup, 2, "PCE Connect");
+ * create_subgroup_counter(events_subgroup, 3, "PCC Disconnect");
+ * create_subgroup_counter(events_subgroup, 4, "PCE Disconnect");
+ *
+ * struct counters_group *cntrs_group = create_counters_group("PCEP Counters",
+ * 3); add_counters_subgroup(cntrs_group, rx_subgroup);
+ * add_counters_subgroup(cntrs_group, tx_subgroup);
+ * add_counters_subgroup(cntrs_group, events_subgroup);
+ */
+
+#define MAX_COUNTER_STR_LENGTH 128
+#define MAX_COUNTER_GROUPS 500
+#define MAX_COUNTERS 500
+
+struct counter {
+ uint16_t counter_id;
+ char counter_name[MAX_COUNTER_STR_LENGTH];
+ uint32_t counter_value;
+};
+
+struct counters_subgroup {
+ char counters_subgroup_name[MAX_COUNTER_STR_LENGTH];
+ uint16_t subgroup_id;
+ uint16_t num_counters;
+ uint16_t max_counters;
+ /* Array of (struct counter *) allocated when the subgroup is created.
+ * The array is indexed by the struct counter->counter_id */
+ struct counter **counters;
+};
+
+struct counters_group {
+ char counters_group_name[MAX_COUNTER_STR_LENGTH];
+ uint16_t num_subgroups;
+ uint16_t max_subgroups;
+ time_t start_time;
+ /* Array of (struct counters_subgroup *) allocated when the group is
+ * created. The subgroup is indexed by the (struct counters_subgroup
+ * *)->subgroup_id */
+ struct counters_subgroup **subgroups;
+};
+
+/*
+ * Create a counters group with the given group_name and number of subgroups.
+ * Subgroup_ids are 0-based, so take that into account when setting
+ * max_subgroups. Return true on success or false if group_name is NULL or
+ * max_subgroups >= MAX_COUNTER_GROUPS.
+ */
+struct counters_group *create_counters_group(const char *group_name,
+ uint16_t max_subgroups);
+
+/*
+ * Create a counters subgroup with the given subgroup_name, subgroup_id and
+ * number of counters. The subgroup_id is 0-based. counter_ids are 0-based, so
+ * take that into account when setting max_counters. Return true on success or
+ * false if subgroup_name is NULL, subgroup_id >= MAX_COUNTER_GROUPS, or
+ * max_counters >= MAX_COUNTERS.
+ */
+struct counters_subgroup *create_counters_subgroup(const char *subgroup_name,
+ uint16_t subgroup_id,
+ uint16_t max_counters);
+
+/*
+ * Add a counter_subgroup to a counter_group.
+ * Return true on success or false if group is NULL or if subgroup is NULL.
+ */
+bool add_counters_subgroup(struct counters_group *group,
+ struct counters_subgroup *subgroup);
+
+/*
+ * Clone a subgroup and set a new name and subgroup_id for the new subgroup.
+ * This is useful for RX and TX counters: just create the RX counters and clone
+ * it for the TX counters.
+ */
+struct counters_subgroup *
+clone_counters_subgroup(struct counters_subgroup *subgroup,
+ const char *subgroup_name, uint16_t subgroup_id);
+
+/*
+ * Create a counter in a subgroup with the given counter_id and counter_name.
+ * The counter_id is 0-based.
+ * Return true on success or false if subgroup is NULL, counter_id >=
+ * MAX_COUNTERS, or if counter_name is NULL.
+ */
+bool create_subgroup_counter(struct counters_subgroup *subgroup,
+ uint32_t counter_id, const char *counter_name);
+
+/*
+ * Delete the counters_group and recursively delete all subgroups and their
+ * counters. Return true on success or false if group is NULL.
+ */
+bool delete_counters_group(struct counters_group *group);
+
+/*
+ * Delete the counters_subgroup and all its counters counters.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool delete_counters_subgroup(struct counters_subgroup *subgroup);
+
+/*
+ * Reset all the counters in all sub-groups contained in this group.
+ * Return true on success or false if group is NULL.
+ */
+bool reset_group_counters(struct counters_group *group);
+
+/*
+ * Reset all the counters in this subgroup.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool reset_subgroup_counters(struct counters_subgroup *subgroup);
+
+/*
+ * Increment a counter given a counter_group, subgroup_id, and counter_id.
+ * Return true on success or false if group is NULL, subgroup_id >=
+ * MAX_COUNTER_GROUPS, or counter_id >= MAX_COUNTERS.
+ */
+bool increment_counter(struct counters_group *group, uint16_t subgroup_id,
+ uint16_t counter_id);
+
+/*
+ * Increment a counter given the counter_subgroup and counter_id.
+ * Return true on success or false if subgroup is NULL or counter_id >=
+ * MAX_COUNTERS.
+ */
+bool increment_subgroup_counter(struct counters_subgroup *subgroup,
+ uint16_t counter_id);
+
+/*
+ * Dump the counter_group info and all its counter_subgroups.
+ * Return true on success or false if group is NULL.
+ */
+bool dump_counters_group_to_log(struct counters_group *group);
+
+/*
+ * Dump all the counters in a counter_subgroup.
+ * Return true on success or false if subgroup is NULL.
+ */
+bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup);
+
+/*
+ * Search for a counters_subgroup by subgroup_id in a counters_group
+ * and return it, if found, else return NULL.
+ */
+struct counters_subgroup *find_subgroup(const struct counters_group *group,
+ uint16_t subgroup_id);
+
+/*
+ * Given a counters_subgroup, return the sum of all the counters.
+ */
+uint32_t subgroup_counters_total(struct counters_subgroup *subgroup);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_ */
diff --git a/pceplib/pcep_utils_double_linked_list.c b/pceplib/pcep_utils_double_linked_list.c
new file mode 100644
index 0000000000..acdcee0598
--- /dev/null
+++ b/pceplib/pcep_utils_double_linked_list.c
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+double_linked_list *dll_initialize()
+{
+ double_linked_list *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list));
+ if (handle != NULL) {
+ memset(handle, 0, sizeof(double_linked_list));
+ handle->num_entries = 0;
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ pcep_log(LOG_WARNING,
+ "%s: dll_initialize cannot allocate memory for handle",
+ __func__);
+ return NULL;
+ }
+
+ return handle;
+}
+
+
+void dll_destroy(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: dll_destroy cannot destroy NULL handle",
+ __func__);
+ return;
+ }
+
+ double_linked_list_node *node = handle->head;
+ while (node != NULL) {
+ double_linked_list_node *node_to_delete = node;
+ node = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node_to_delete);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void dll_destroy_with_data_memtype(double_linked_list *handle,
+ void *data_memory_type)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: dll_destroy_with_data cannot destroy NULL handle",
+ __func__);
+ return;
+ }
+
+ double_linked_list_node *node = handle->head;
+ while (node != NULL) {
+ double_linked_list_node *node_to_delete = node;
+ pceplib_free(data_memory_type, node->data);
+ node = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node_to_delete);
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void dll_destroy_with_data(double_linked_list *handle)
+{
+ /* Default to destroying the data with the INFRA mem type */
+ dll_destroy_with_data_memtype(handle, PCEPLIB_INFRA);
+}
+
+
+/* Creates a node and adds it as the first item in the list */
+double_linked_list_node *dll_prepend(double_linked_list *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_prepend_data NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ /* Create the new node */
+ double_linked_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node));
+ memset(new_node, 0, sizeof(double_linked_list_node));
+ new_node->data = data;
+
+ if (handle->head == NULL) {
+ handle->head = new_node;
+ handle->tail = new_node;
+ } else {
+ new_node->next_node = handle->head;
+ handle->head->prev_node = new_node;
+ handle->head = new_node;
+ }
+
+ (handle->num_entries)++;
+
+ return new_node;
+}
+
+
+/* Creates a node and adds it as the last item in the list */
+double_linked_list_node *dll_append(double_linked_list *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_append_data NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ /* Create the new node */
+ double_linked_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node));
+ memset(new_node, 0, sizeof(double_linked_list_node));
+ new_node->data = data;
+
+ if (handle->head == NULL) {
+ handle->head = new_node;
+ handle->tail = new_node;
+ } else {
+ new_node->prev_node = handle->tail;
+ handle->tail->next_node = new_node;
+ handle->tail = new_node;
+ }
+
+ (handle->num_entries)++;
+
+ return new_node;
+}
+
+
+/* Delete the first node in the list, and return the data */
+void *dll_delete_first_node(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_first_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *delete_node = handle->head;
+ void *data = delete_node->data;
+
+ if (delete_node->next_node == NULL) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ handle->head = delete_node->next_node;
+ handle->head->prev_node = NULL;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, delete_node);
+ (handle->num_entries)--;
+
+ return data;
+}
+
+
+/* Delete the last node in the list, and return the data */
+void *dll_delete_last_node(double_linked_list *handle)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_last_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ double_linked_list_node *delete_node = handle->tail;
+ void *data = delete_node->data;
+
+ if (delete_node->prev_node == NULL) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else {
+ handle->tail = delete_node->prev_node;
+ handle->tail->next_node = NULL;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, delete_node);
+ (handle->num_entries)--;
+
+ return data;
+}
+
+
+/* Delete the designated node in the list, and return the data */
+void *dll_delete_node(double_linked_list *handle, double_linked_list_node *node)
+{
+ if (handle == NULL) {
+ pcep_log(LOG_WARNING, "%s: dll_delete_node NULL handle",
+ __func__);
+ return NULL;
+ }
+
+ if (node == NULL) {
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *data = node->data;
+
+ if (handle->head == handle->tail) {
+ /* Its the last node in the list */
+ handle->head = NULL;
+ handle->tail = NULL;
+ } else if (handle->head == node) {
+ handle->head = node->next_node;
+ handle->head->prev_node = NULL;
+ } else if (handle->tail == node) {
+ handle->tail = node->prev_node;
+ handle->tail->next_node = NULL;
+ } else {
+ /* Its somewhere in the middle of the list */
+ node->next_node->prev_node = node->prev_node;
+ node->prev_node->next_node = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+ (handle->num_entries)--;
+
+ return data;
+}
diff --git a/pceplib/pcep_utils_double_linked_list.h b/pceplib/pcep_utils_double_linked_list.h
new file mode 100644
index 0000000000..4fe01726ad
--- /dev/null
+++ b/pceplib/pcep_utils_double_linked_list.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_
+
+typedef struct double_linked_list_node_ {
+ struct double_linked_list_node_ *prev_node;
+ struct double_linked_list_node_ *next_node;
+ void *data;
+
+} double_linked_list_node;
+
+
+typedef struct double_linked_list_ {
+ double_linked_list_node *head;
+ double_linked_list_node *tail;
+ unsigned int num_entries;
+
+} double_linked_list;
+
+
+/* Initialize a double linked list */
+double_linked_list *dll_initialize(void);
+
+/* Destroy a double linked list, by freeing the handle and nodes,
+ * user data will not be freed, and may be leaked if not handled
+ * externally. */
+void dll_destroy(double_linked_list *handle);
+/* Destroy a double linked list, by freeing the handle and nodes,
+ * and the user data. */
+void dll_destroy_with_data(double_linked_list *handle);
+void dll_destroy_with_data_memtype(double_linked_list *handle,
+ void *data_memory_type);
+
+/* Creates a node and adds it as the first item in the list */
+double_linked_list_node *dll_prepend(double_linked_list *handle, void *data);
+
+/* Creates a node and adds it as the last item in the list */
+double_linked_list_node *dll_append(double_linked_list *handle, void *data);
+
+/* Delete the first node in the list, and return the data */
+void *dll_delete_first_node(double_linked_list *handle);
+
+/* Delete the last node in the list, and return the data */
+void *dll_delete_last_node(double_linked_list *handle);
+
+/* Delete the designated node in the list, and return the data */
+void *dll_delete_node(double_linked_list *handle,
+ double_linked_list_node *node);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_ */
diff --git a/pceplib/pcep_utils_logging.c b/pceplib/pcep_utils_logging.c
new file mode 100644
index 0000000000..65e1abbc03
--- /dev/null
+++ b/pceplib/pcep_utils_logging.c
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "pcep_utils_logging.h"
+
+/* Forward declaration */
+int pcep_stdout_logger(int priority, const char *format, va_list args);
+
+static pcep_logger_func logger_func = pcep_stdout_logger;
+static int logging_level_ = LOG_INFO;
+
+void register_logger(pcep_logger_func logger)
+{
+ logger_func = logger;
+}
+
+void set_logging_level(int level)
+{
+ logging_level_ = level;
+}
+
+int get_logging_level()
+{
+ return logging_level_;
+}
+
+void pcep_log(int priority, const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ logger_func(priority, format, va);
+ va_end(va);
+}
+
+void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes,
+ uint8_t bytes_len)
+{
+ char byte_str[2048] = {0};
+ int i = 0;
+
+ snprintf(byte_str, 2048, "%s ", message);
+ for (; i < bytes_len; i++) {
+ snprintf(byte_str, 2048, "%02x ", bytes[i]);
+ }
+ snprintf(byte_str, 2048, "\n");
+
+ pcep_log(priority, "%s", byte_str);
+}
+
+/* Defined with a return type to match the FRR logging signature.
+ * Assuming glibc printf() is thread-safe. */
+int pcep_stdout_logger(int priority, const char *format, va_list args)
+{
+ if (priority <= logging_level_) {
+ vprintf(format, args);
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/pceplib/pcep_utils_logging.h b/pceplib/pcep_utils_logging.h
new file mode 100644
index 0000000000..24ea495bd8
--- /dev/null
+++ b/pceplib/pcep_utils_logging.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_
+
+#include <syslog.h> /* Logging levels */
+#include <stdarg.h> /* va_list */
+#include <stdint.h> /* uint8_t */
+
+/*
+ * The logging defined here i intended to provide the infrastructure to
+ * be able to plug-in an external logger, primarily the FRR logger. There
+ * will be a default internal logger implemented that will write to stdout,
+ * but any other advanced logging features should be implemented externally.
+ */
+
+/* Only the following logging levels from syslog.h should be used:
+ *
+ * LOG_DEBUG - For all messages that are enabled by optional debugging
+ * features, typically preceded by "if (IS...DEBUG...)"
+ * LOG_INFO - Information that may be of interest, but
+ * everything seems to be working properly.
+ * LOG_NOTICE - Only for message pertaining to daemon startup or shutdown.
+ * LOG_WARNING - Warning conditions: unexpected events, but the daemon
+ * believes it can continue to operate correctly.
+ * LOG_ERR - Error situations indicating malfunctions.
+ * Probably requires attention.
+ */
+
+
+/* The signature of this logger function is the same as the FRR logger */
+typedef int (*pcep_logger_func)(int, const char *, va_list);
+void register_logger(pcep_logger_func logger);
+
+/* These functions only take affect when using the internal stdout logger */
+void set_logging_level(int level);
+int get_logging_level(void);
+
+/* Log messages either to a previously registered
+ * logger or to the internal default stdout logger. */
+void pcep_log(int priority, const char *format, ...);
+void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes,
+ uint8_t bytes_len);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_ */
diff --git a/pceplib/pcep_utils_memory.c b/pceplib/pcep_utils_memory.c
new file mode 100644
index 0000000000..7362e3433b
--- /dev/null
+++ b/pceplib/pcep_utils_memory.c
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/* Set default values for memory function pointers */
+static pceplib_malloc_func mfunc = NULL;
+static pceplib_calloc_func cfunc = NULL;
+static pceplib_realloc_func rfunc = NULL;
+static pceplib_strdup_func sfunc = NULL;
+static pceplib_free_func ffunc = NULL;
+
+/* Internal memory types */
+struct pceplib_memory_type pceplib_infra_mt = {
+ .memory_type_name = "PCEPlib Infrastructure memory",
+ .total_bytes_allocated = 0,
+ .num_allocates = 0,
+ .total_bytes_freed = 0,
+ .num_frees = 0};
+struct pceplib_memory_type pceplib_messages_mt = {
+ .memory_type_name = "PCEPlib Messages memory",
+ .total_bytes_allocated = 0,
+ .num_allocates = 0,
+ .total_bytes_freed = 0,
+ .num_frees = 0};
+
+/* The memory type pointers default to the internal memory types */
+void *PCEPLIB_INFRA = &pceplib_infra_mt;
+void *PCEPLIB_MESSAGES = &pceplib_messages_mt;
+
+/* Initialize memory function pointers and memory type pointers */
+bool pceplib_memory_initialize(void *pceplib_infra_mt,
+ void *pceplib_messages_mt,
+ pceplib_malloc_func mf, pceplib_calloc_func cf,
+ pceplib_realloc_func rf, pceplib_strdup_func sf,
+ pceplib_free_func ff)
+{
+ PCEPLIB_INFRA = (pceplib_infra_mt ? pceplib_infra_mt : PCEPLIB_INFRA);
+ PCEPLIB_MESSAGES =
+ (pceplib_messages_mt ? pceplib_messages_mt : PCEPLIB_MESSAGES);
+
+ mfunc = (mf ? mf : mfunc);
+ cfunc = (cf ? cf : cfunc);
+ rfunc = (rf ? rf : rfunc);
+ sfunc = (sf ? sf : sfunc);
+ ffunc = (ff ? ff : ffunc);
+
+ return true;
+}
+
+void pceplib_memory_reset()
+{
+ pceplib_infra_mt.total_bytes_allocated = 0;
+ pceplib_infra_mt.num_allocates = 0;
+ pceplib_infra_mt.total_bytes_freed = 0;
+ pceplib_infra_mt.num_frees = 0;
+
+ pceplib_messages_mt.total_bytes_allocated = 0;
+ pceplib_messages_mt.num_allocates = 0;
+ pceplib_messages_mt.total_bytes_freed = 0;
+ pceplib_messages_mt.num_frees = 0;
+}
+
+void pceplib_memory_dump()
+{
+ if (PCEPLIB_INFRA) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]",
+ __func__,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->total_bytes_allocated,
+ ((struct pceplib_memory_type *)PCEPLIB_INFRA)
+ ->num_frees);
+ }
+
+ if (PCEPLIB_MESSAGES) {
+ pcep_log(
+ LOG_INFO,
+ "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]",
+ __func__,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->total_bytes_allocated,
+ ((struct pceplib_memory_type *)PCEPLIB_MESSAGES)
+ ->num_frees);
+ }
+}
+
+/* PCEPlib memory functions:
+ * They either call the supplied function pointers, or use the internal
+ * implementations, which just increment simple counters and call the
+ * C stdlib memory implementations. */
+
+void *pceplib_malloc(void *mem_type, size_t size)
+{
+ if (mfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return malloc(size);
+ } else {
+ return mfunc(mem_type, size);
+ }
+}
+
+void *pceplib_calloc(void *mem_type, size_t size)
+{
+ if (cfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return calloc(1, size);
+ } else {
+ return cfunc(mem_type, size);
+ }
+}
+
+void *pceplib_realloc(void *mem_type, void *ptr, size_t size)
+{
+ if (rfunc == NULL) {
+ if (mem_type != NULL) {
+ /* TODO should add previous allocated bytes to
+ * total_bytes_freed */
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += size;
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return realloc(ptr, size);
+ } else {
+ return rfunc(mem_type, ptr, size);
+ }
+}
+
+void *pceplib_strdup(void *mem_type, const char *str)
+{
+ if (sfunc == NULL) {
+ if (mem_type != NULL) {
+ ((struct pceplib_memory_type *)mem_type)
+ ->total_bytes_allocated += strlen(str);
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates++;
+ }
+
+ return strdup(str);
+ } else {
+ return sfunc(mem_type, str);
+ }
+}
+
+void pceplib_free(void *mem_type, void *ptr)
+{
+ if (ffunc == NULL) {
+ if (mem_type != NULL) {
+ /* TODO in order to increment total_bytes_freed, we need
+ * to keep track of the bytes allocated per pointer.
+ * Currently not implemented. */
+ ((struct pceplib_memory_type *)mem_type)->num_frees++;
+ if (((struct pceplib_memory_type *)mem_type)
+ ->num_allocates
+ < ((struct pceplib_memory_type *)mem_type)
+ ->num_frees) {
+ pcep_log(
+ LOG_ERR,
+ "%s: pceplib_free MT N_Alloc < N_Free: MemType [%s] NumAllocates [%d] NumFrees [%d]",
+ __func__,
+ ((struct pceplib_memory_type *)mem_type)
+ ->memory_type_name,
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_allocates,
+ ((struct pceplib_memory_type *)mem_type)
+ ->num_frees);
+ }
+ }
+
+ return free(ptr);
+ } else {
+ return ffunc(mem_type, ptr);
+ }
+}
diff --git a/pceplib/pcep_utils_memory.h b/pceplib/pcep_utils_memory.h
new file mode 100644
index 0000000000..4624a91a1c
--- /dev/null
+++ b/pceplib/pcep_utils_memory.h
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_
+#define PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* This module is intended to be used primarily with FRR's memory module,
+ * which has memory groups and memory types, although any memory infrastructure
+ * can be used that has memory types or the memory types in this module can be
+ * set to NULL. The PCEPlib can be used stand-alone, in which case the simple
+ * internal memory type system will be used.
+ */
+
+/* These memory function pointers are modeled after the memory functions
+ * in frr/lib/memory.h */
+typedef void *(*pceplib_malloc_func)(void *mem_type, size_t size);
+typedef void *(*pceplib_calloc_func)(void *mem_type, size_t size);
+typedef void *(*pceplib_realloc_func)(void *mem_type, void *ptr, size_t size);
+typedef void *(*pceplib_strdup_func)(void *mem_type, const char *str);
+typedef void (*pceplib_free_func)(void *mem_type, void *ptr);
+
+/* Either an internal pceplib_memory_type pointer
+ * or could be an FRR memory type pointer */
+extern void *PCEPLIB_INFRA;
+extern void *PCEPLIB_MESSAGES;
+
+/* Internal PCEPlib memory type */
+struct pceplib_memory_type {
+ char memory_type_name[64];
+ uint32_t total_bytes_allocated;
+ uint32_t num_allocates;
+ uint32_t total_bytes_freed; /* currently not used */
+ uint32_t num_frees;
+};
+
+/* Initialize this module by passing in the 2 memory types used in the PCEPlib
+ * and by passing in the different memory allocation/free function pointers.
+ * Any of these parameters can be NULL, in which case an internal implementation
+ * will be used.
+ */
+bool pceplib_memory_initialize(void *pceplib_infra_mt,
+ void *pceplib_messages_mt,
+ pceplib_malloc_func mfunc,
+ pceplib_calloc_func cfunc,
+ pceplib_realloc_func rfunc,
+ pceplib_strdup_func sfunc,
+ pceplib_free_func ffunc);
+
+/* Reset the internal allocation/free counters. Used mainly for internal
+ * testing. */
+void pceplib_memory_reset(void);
+void pceplib_memory_dump(void);
+
+/* Memory functions to be used throughout the PCEPlib. Internally, these
+ * functions will either used the function pointers passed in via
+ * pceplib_memory_initialize() or a simple internal implementation. The
+ * internal implementations just increment the internal memory type
+ * counters and call the C stdlib memory functions.
+ */
+void *pceplib_malloc(void *mem_type, size_t size);
+void *pceplib_calloc(void *mem_type, size_t size);
+void *pceplib_realloc(void *mem_type, void *ptr, size_t size);
+void *pceplib_strdup(void *mem_type, const char *str);
+void pceplib_free(void *mem_type, void *ptr);
+
+#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_ */
diff --git a/pceplib/pcep_utils_ordered_list.c b/pceplib/pcep_utils_ordered_list.c
new file mode 100644
index 0000000000..f5c7f70240
--- /dev/null
+++ b/pceplib/pcep_utils_ordered_list.c
@@ -0,0 +1,322 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_ordered_list.h"
+
+/* Compare function that simply compares pointers.
+ * return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+int pointer_compare_function(void *list_entry, void *new_entry)
+{
+ return (char *)new_entry - (char *)list_entry;
+}
+
+ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr)
+{
+ ordered_list_handle *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_handle));
+ memset(handle, 0, sizeof(ordered_list_handle));
+ handle->head = NULL;
+ handle->num_entries = 0;
+ handle->compare_function = func_ptr;
+
+ return handle;
+}
+
+
+/* free all the ordered_list_node resources and the ordered_list_handle.
+ * it is assumed that the user is responsible fore freeing the data
+ * pointed to by the nodes.
+ */
+void ordered_list_destroy(ordered_list_handle *handle)
+{
+ if (handle == NULL) {
+ return;
+ }
+
+ ordered_list_node *node = handle->head;
+ ordered_list_node *next;
+
+ while (node != NULL) {
+ next = node->next_node;
+ pceplib_free(PCEPLIB_INFRA, node);
+ node = next;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+ordered_list_node *ordered_list_add_node(ordered_list_handle *handle,
+ void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_add_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+ handle->num_entries++;
+
+ ordered_list_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_node));
+ memset(new_node, 0, sizeof(ordered_list_node));
+ new_node->data = data;
+ new_node->next_node = NULL;
+
+ /* check if its an empty list */
+ if (handle->head == NULL) {
+ handle->head = new_node;
+
+ return new_node;
+ }
+
+ ordered_list_node *prev_node = handle->head;
+ ordered_list_node *node = prev_node;
+ int compare_result;
+
+ while (node != NULL) {
+ compare_result = handle->compare_function(node->data, data);
+ if (compare_result < 0) {
+ /* insert the node */
+ new_node->next_node = node;
+ if (handle->head == node) {
+ /* add it at the beginning of the list */
+ handle->head = new_node;
+ } else {
+ prev_node->next_node = new_node;
+ }
+
+ return new_node;
+ }
+
+ /* keep searching with the next node in the list */
+ prev_node = node;
+ node = node->next_node;
+ }
+
+ /* at the end of the list, add it here */
+ prev_node->next_node = new_node;
+
+ return new_node;
+}
+
+
+ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_find2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ ordered_list_node *node = handle->head;
+ int compare_result;
+
+ while (node != NULL) {
+ compare_result = compare_func(node->data, data);
+ if (compare_result == 0) {
+ return node;
+ } else {
+ node = node->next_node;
+ }
+ }
+
+ return NULL;
+}
+
+
+ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_find, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ return ordered_list_find2(handle, data, handle->compare_function);
+}
+
+
+void *ordered_list_remove_first_node(ordered_list_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+ handle->num_entries--;
+
+ void *data = handle->head->data;
+ ordered_list_node *next_node = handle->head->next_node;
+ pceplib_free(PCEPLIB_INFRA, handle->head);
+ handle->head = next_node;
+
+ return data;
+}
+
+
+void *
+ordered_list_remove_first_node_equals2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node_equals2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ ordered_list_node *prev_node = handle->head;
+ ordered_list_node *node = prev_node;
+ bool keep_walking = true;
+ void *return_data = NULL;
+ int compare_result;
+
+ while (node != NULL && keep_walking) {
+ compare_result = compare_func(node->data, data);
+ if (compare_result == 0) {
+ return_data = node->data;
+ keep_walking = false;
+ handle->num_entries--;
+
+ /* adjust the corresponding pointers accordingly */
+ if (handle->head == node) {
+ /* its the first node in the list */
+ handle->head = node->next_node;
+ } else {
+ prev_node->next_node = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+ } else {
+ prev_node = node;
+ node = node->next_node;
+ }
+ }
+
+ return return_data;
+}
+
+
+void *ordered_list_remove_first_node_equals(ordered_list_handle *handle,
+ void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_first_node_equals, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ return ordered_list_remove_first_node_equals2(handle, data,
+ handle->compare_function);
+}
+
+
+void *ordered_list_remove_node(ordered_list_handle *handle,
+ ordered_list_node *prev_node,
+ ordered_list_node *node_toRemove)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_node, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *return_data = node_toRemove->data;
+ handle->num_entries--;
+
+ if (node_toRemove == handle->head) {
+ handle->head = node_toRemove->next_node;
+ } else {
+ prev_node->next_node = node_toRemove->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node_toRemove);
+
+ return return_data;
+}
+
+void *ordered_list_remove_node2(ordered_list_handle *handle,
+ ordered_list_node *node_to_remove)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_WARNING,
+ "%s: ordered_list_remove_node2, the list has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ ordered_list_node *node = handle->head;
+ ordered_list_node *prev_node = handle->head;
+
+ while (node != NULL) {
+ if (node == node_to_remove) {
+ return (ordered_list_remove_node(handle, prev_node,
+ node));
+ } else {
+ prev_node = node;
+ node = node->next_node;
+ }
+ }
+
+ return NULL;
+}
diff --git a/pceplib/pcep_utils_ordered_list.h b/pceplib/pcep_utils_ordered_list.h
new file mode 100644
index 0000000000..ec132dc164
--- /dev/null
+++ b/pceplib/pcep_utils_ordered_list.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPUTILSORDEREDLIST_H_
+#define INCLUDE_PCEPUTILSORDEREDLIST_H_
+
+#include <stdbool.h>
+
+typedef struct ordered_list_node_ {
+ struct ordered_list_node_ *next_node;
+ void *data;
+
+} ordered_list_node;
+
+/* The implementation of this function will receive a pointer to the
+ * new data to be inserted and a pointer to the list_entry, and should
+ * return:
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+typedef int (*ordered_compare_function)(void *list_entry, void *new_entry);
+
+/* Compare function that compares pointers */
+int pointer_compare_function(void *list_entry, void *new_entry);
+
+typedef struct ordered_list_handle_ {
+ ordered_list_node *head;
+ unsigned int num_entries;
+ ordered_compare_function compare_function;
+
+} ordered_list_handle;
+
+ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr);
+void ordered_list_destroy(ordered_list_handle *handle);
+
+/* Add a new ordered_list_node to the list, using the ordered_compare_function
+ * to determine where in the list to add it. The newly created ordered_list_node
+ * will be returned.
+ */
+ordered_list_node *ordered_list_add_node(ordered_list_handle *handle,
+ void *data);
+
+/* Find an entry in the ordered_list using the ordered_compare_function to
+ * compare the data passed in.
+ * Return the node if found, NULL otherwise.
+ */
+ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data);
+
+/* The same as the previous function, but with a specific orderedComparefunction
+ */
+ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data,
+ ordered_compare_function compare_func);
+
+/* Remove the first entry in the list and return the data it points to.
+ * Will return NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_first_node(ordered_list_handle *handle);
+
+/* Remove the first entry in the list that has the same data, using the
+ * ordered_compare_function, and return the data it points to.
+ * Will return NULL if the handle is NULL or if the list is empty or
+ * if no entry is found that equals data.
+ */
+void *ordered_list_remove_first_node_equals(ordered_list_handle *handle,
+ void *data);
+
+/* The same as the previous function, but with a specific orderedComparefunction
+ */
+void *ordered_list_remove_first_node_equals2(ordered_list_handle *handle,
+ void *data,
+ ordered_compare_function func_ptr);
+
+/* Remove the node "node_to_remove" and adjust the "prev_node" pointers
+ * accordingly, returning the data pointed to by "node_to_remove". Will return
+ * NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_node(ordered_list_handle *handle,
+ ordered_list_node *prev_node,
+ ordered_list_node *node_to_remove);
+
+/* Remove the node "node_to_remove" by searching for it in the entire list,
+ * returning the data pointed to by "node_to_remove".
+ * Will return NULL if the handle is NULL or if the list is empty.
+ */
+void *ordered_list_remove_node2(ordered_list_handle *handle,
+ ordered_list_node *node_to_remove);
+
+#endif /* INCLUDE_PCEPUTILSORDEREDLIST_H_ */
diff --git a/pceplib/pcep_utils_queue.c b/pceplib/pcep_utils_queue.c
new file mode 100644
index 0000000000..e8c3f2be0e
--- /dev/null
+++ b/pceplib/pcep_utils_queue.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+#include "pcep_utils_queue.h"
+
+queue_handle *queue_initialize()
+{
+ /* Set the max_entries to 0 to disable it */
+ return queue_initialize_with_size(0);
+}
+
+
+queue_handle *queue_initialize_with_size(unsigned int max_entries)
+{
+ queue_handle *handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_handle));
+ memset(handle, 0, sizeof(queue_handle));
+ handle->max_entries = max_entries;
+
+ return handle;
+}
+
+
+void queue_destroy(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_destroy, the queue has not been initialized",
+ __func__);
+ return;
+ }
+
+ while (queue_dequeue(handle) != NULL) {
+ }
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+void queue_destroy_with_data(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_destroy_with_data, the queue has not been initialized",
+ __func__);
+ return;
+ }
+
+ void *data = queue_dequeue(handle);
+ while (data != NULL) {
+ pceplib_free(PCEPLIB_INFRA, data);
+ data = queue_dequeue(handle);
+ }
+ pceplib_free(PCEPLIB_INFRA, handle);
+}
+
+
+queue_node *queue_enqueue(queue_handle *handle, void *data)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_enqueue, the queue has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->max_entries > 0
+ && handle->num_entries >= handle->max_entries) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_enqueue, cannot enqueue: max entries hit [%u]",
+ handle->num_entries);
+ return NULL;
+ }
+
+ queue_node *new_node =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_node));
+ memset(new_node, 0, sizeof(queue_node));
+ new_node->data = data;
+ new_node->next_node = NULL;
+
+ (handle->num_entries)++;
+ if (handle->head == NULL) {
+ /* its the first entry in the queue */
+ handle->head = handle->tail = new_node;
+ } else {
+ handle->tail->next_node = new_node;
+ handle->tail = new_node;
+ }
+
+ return new_node;
+}
+
+
+void *queue_dequeue(queue_handle *handle)
+{
+ if (handle == NULL) {
+ pcep_log(
+ LOG_DEBUG,
+ "%s: queue_dequeue, the queue has not been initialized",
+ __func__);
+ return NULL;
+ }
+
+ if (handle->head == NULL) {
+ return NULL;
+ }
+
+ void *node_data = handle->head->data;
+ queue_node *node = handle->head;
+ (handle->num_entries)--;
+ if (handle->head == handle->tail) {
+ /* its the last entry in the queue */
+ handle->head = handle->tail = NULL;
+ } else {
+ handle->head = node->next_node;
+ }
+
+ pceplib_free(PCEPLIB_INFRA, node);
+
+ return node_data;
+}
diff --git a/pceplib/pcep_utils_queue.h b/pceplib/pcep_utils_queue.h
new file mode 100644
index 0000000000..838067640e
--- /dev/null
+++ b/pceplib/pcep_utils_queue.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#ifndef INCLUDE_PCEPUTILSQUEUE_H_
+#define INCLUDE_PCEPUTILSQUEUE_H_
+
+typedef struct queue_node_ {
+ struct queue_node_ *next_node;
+ void *data;
+
+} queue_node;
+
+typedef struct queue_handle_ {
+ queue_node *head;
+ queue_node *tail;
+ unsigned int num_entries;
+ /* Set to 0 to disable */
+ unsigned int max_entries;
+
+} queue_handle;
+
+queue_handle *queue_initialize(void);
+queue_handle *queue_initialize_with_size(unsigned int max_entries);
+void queue_destroy(queue_handle *handle);
+void queue_destroy_with_data(queue_handle *handle);
+queue_node *queue_enqueue(queue_handle *handle, void *data);
+void *queue_dequeue(queue_handle *handle);
+
+#endif /* INCLUDE_PCEPUTILSQUEUE_H_ */
diff --git a/pceplib/subdir.am b/pceplib/subdir.am
new file mode 100644
index 0000000000..eee2ec28c7
--- /dev/null
+++ b/pceplib/subdir.am
@@ -0,0 +1,62 @@
+if PATHD_PCEP
+
+noinst_LTLIBRARIES = pceplib/libpcep_pcc.la pceplib/libsocket_comm_mock.la
+pceplib_libpcep_pcc_la_CFLAGS = -fPIC
+pceplib_libpcep_pcc_la_SOURCES = pceplib/pcep_msg_messages.c \
+ pceplib/pcep_msg_objects.c \
+ pceplib/pcep_msg_tlvs.c \
+ pceplib/pcep_msg_tools.c \
+ pceplib/pcep_msg_messages_encoding.c \
+ pceplib/pcep_msg_objects_encoding.c \
+ pceplib/pcep_msg_tlvs_encoding.c \
+ pceplib/pcep_msg_object_error_types.c \
+ pceplib/pcep_pcc_api.c \
+ pceplib/pcep_session_logic.c \
+ pceplib/pcep_session_logic_loop.c \
+ pceplib/pcep_session_logic_states.c \
+ pceplib/pcep_session_logic_counters.c \
+ pceplib/pcep_socket_comm_loop.c \
+ pceplib/pcep_socket_comm.c \
+ pceplib/pcep_timers_event_loop.c \
+ pceplib/pcep_timers.c \
+ pceplib/pcep_utils_counters.c \
+ pceplib/pcep_utils_double_linked_list.c \
+ pceplib/pcep_utils_logging.c \
+ pceplib/pcep_utils_memory.c \
+ pceplib/pcep_utils_ordered_list.c \
+ pceplib/pcep_utils_queue.c
+
+if PATHD_PCEP_TEST
+# SocketComm Mock library used for Unit Testing
+pceplib_libsocket_comm_mock_la_SOURCES = pceplib/pcep_socket_comm_mock.c
+endif
+
+noinst_HEADERS += pceplib/pcep.h \
+ pceplib/pcep_msg_encoding.h \
+ pceplib/pcep_msg_messages.h \
+ pceplib/pcep_msg_object_error_types.h \
+ pceplib/pcep_msg_objects.h \
+ pceplib/pcep_msg_tlvs.h \
+ pceplib/pcep_msg_tools.h \
+ pceplib/pcep_pcc_api.h \
+ pceplib/pcep_session_logic.h \
+ pceplib/pcep_session_logic_internals.h \
+ pceplib/pcep_socket_comm.h \
+ pceplib/pcep_socket_comm_internals.h \
+ pceplib/pcep_socket_comm_loop.h \
+ pceplib/pcep_socket_comm_mock.h \
+ pceplib/pcep_timer_internals.h \
+ pceplib/pcep_timers.h \
+ pceplib/pcep_timers_event_loop.h \
+ pceplib/pcep_utils_counters.h \
+ pceplib/pcep_utils_double_linked_list.h \
+ pceplib/pcep_utils_logging.h \
+ pceplib/pcep_utils_memory.h \
+ pceplib/pcep_utils_ordered_list.h \
+ pceplib/pcep_utils_queue.h
+
+noinst_PROGRAMS += pceplib/pcep_pcc
+pceplib_pcep_pcc_SOURCES = pceplib/pcep_pcc.c
+pceplib_pcep_pcc_LDADD = pceplib/libpcep_pcc.la lib/libfrr.la -lpthread
+
+endif
diff --git a/pceplib/test/pcep_msg_messages_test.c b/pceplib/test/pcep_msg_messages_test.c
new file mode 100644
index 0000000000..10b678bcec
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_test.c
@@ -0,0 +1,498 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_messages_test.h"
+
+/*
+ * Notice:
+ * All of these message Unit Tests encode the created messages by explicitly
+ * calling pcep_encode_message() thus testing the message creation and the
+ * message encoding.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+
+int pcep_messages_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_messages_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_messages_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+}
+
+void pcep_messages_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+void test_pcep_msg_create_open()
+{
+ uint8_t keepalive = 30;
+ uint8_t deadtimer = 60;
+ uint8_t sid = 255;
+
+ struct pcep_message *message =
+ pcep_msg_create_open(keepalive, deadtimer, sid);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_OPEN,
+ PCEP_OBJ_TYPE_OPEN));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_open *open_obj =
+ (struct pcep_object_open *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(open_obj->header.object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(open_obj->header.object_type, PCEP_OBJ_TYPE_OPEN);
+
+ CU_ASSERT_EQUAL(open_obj->open_deadtimer, deadtimer);
+ CU_ASSERT_EQUAL(open_obj->open_keepalive, keepalive);
+ CU_ASSERT_EQUAL(open_obj->open_sid, sid);
+ CU_ASSERT_EQUAL(open_obj->open_version, PCEP_OBJECT_OPEN_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_request()
+{
+ /* First test with NULL objects */
+ struct pcep_message *message =
+ pcep_msg_create_request(NULL, NULL, NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ /* Test IPv4 */
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct in_addr src_addr, dst_addr;
+ struct pcep_object_endpoints_ipv4 *ipv4_obj =
+ pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr);
+ message = pcep_msg_create_request(rp_obj, ipv4_obj, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv4_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ /* Test IPv6 */
+ rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct in6_addr src_addr_ipv6, dst_addr_ipv6;
+ struct pcep_object_endpoints_ipv6 *ipv6_obj =
+ pcep_obj_create_endpoint_ipv6(&src_addr_ipv6, &dst_addr_ipv6);
+ message = pcep_msg_create_request_ipv6(rp_obj, ipv6_obj, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv6_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ /* The objects get deleted with the message, so they need to be created
+ * again */
+ rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ ipv4_obj = pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr);
+ struct pcep_object_bandwidth *bandwidth_obj =
+ pcep_obj_create_bandwidth(4.2);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, bandwidth_obj);
+ message = pcep_msg_create_request(rp_obj, ipv4_obj, obj_list);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(
+ message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&ipv4_obj->header)
+ + pcep_object_get_length_by_hdr(
+ &bandwidth_obj->header));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_request_svec()
+{
+}
+
+
+void test_pcep_msg_create_reply_nopath()
+{
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ struct pcep_object_nopath *nopath_obj = pcep_obj_create_nopath(
+ false, false, PCEP_NOPATH_TLV_ERR_NO_TLV);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, nopath_obj);
+
+ struct pcep_message *message = pcep_msg_create_reply(rp_obj, obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ (MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + pcep_object_get_length_by_hdr(&nopath_obj->header)));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_reply()
+{
+ /* First test with NULL ero and rp objects */
+ struct pcep_message *message = pcep_msg_create_reply(NULL, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 0);
+ CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+
+ double_linked_list *ero_subobj_list = dll_initialize();
+ struct pcep_object_ro_subobj *ero_subobj =
+ (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_32label(true, 1, 10);
+ dll_append(ero_subobj_list, ero_subobj);
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ double_linked_list *object_list = dll_initialize();
+ dll_append(object_list, ero);
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ message = pcep_msg_create_reply(rp_obj, object_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length_by_hdr(&rp_obj->header)
+ + OBJECT_HEADER_LENGTH
+ + OBJECT_RO_SUBOBJ_HEADER_LENGTH
+ + 6 /* size of the 32label */);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_close()
+{
+ uint8_t reason = PCEP_CLOSE_REASON_UNREC_MSG;
+
+ struct pcep_message *message = pcep_msg_create_close(reason);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_CLOSE,
+ PCEP_OBJ_TYPE_CLOSE));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_CLOSE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_close *close_obj =
+ (struct pcep_object_close *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(close_obj->header.object_class, PCEP_OBJ_CLASS_CLOSE);
+ CU_ASSERT_EQUAL(close_obj->header.object_type, PCEP_OBJ_TYPE_CLOSE);
+ CU_ASSERT_EQUAL(close_obj->reason, reason);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_error()
+{
+ uint8_t error_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT;
+ uint8_t error_value = PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT;
+
+ struct pcep_message *message =
+ pcep_msg_create_error(error_type, error_value);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + pcep_object_get_length(PCEP_OBJ_CLASS_ERROR,
+ PCEP_OBJ_TYPE_ERROR));
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_ERROR);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ /* Just check the class and type, the rest of the hdr fields
+ * are verified in pcep-objects-test.c */
+ struct pcep_object_error *error_obj =
+ (struct pcep_object_error *)message->obj_list->head->data;
+ CU_ASSERT_EQUAL(error_obj->header.object_class, PCEP_OBJ_CLASS_ERROR);
+ CU_ASSERT_EQUAL(error_obj->header.object_type, PCEP_OBJ_TYPE_ERROR);
+
+ CU_ASSERT_EQUAL(error_obj->error_type, error_type);
+ CU_ASSERT_EQUAL(error_obj->error_value, error_value);
+ pcep_msg_free_message(message);
+}
+
+
+void test_pcep_msg_create_keepalive()
+{
+ struct pcep_message *message = pcep_msg_create_keepalive();
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 0);
+ CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_KEEPALIVE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_report()
+{
+ double_linked_list *obj_list = dll_initialize();
+
+ /* Should return NULL if obj_list is empty */
+ struct pcep_message *message = pcep_msg_create_report(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(obj_list, lsp);
+ message = pcep_msg_create_report(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + lsp->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_REPORT);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_update()
+{
+ double_linked_list *obj_list = dll_initialize();
+ double_linked_list *ero_subobj_list = dll_initialize();
+
+ struct pcep_message *message = pcep_msg_create_update(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ /* Should return NULL if obj_list is empty */
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ /* Should return NULL if obj_list does not have 3 entries */
+ dll_append(obj_list, srp);
+ dll_append(obj_list, lsp);
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ dll_append(obj_list, ero);
+ message = pcep_msg_create_update(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + srp->header.encoded_object_length
+ + lsp->header.encoded_object_length
+ + ero->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_initiate()
+{
+ double_linked_list *obj_list = dll_initialize();
+ double_linked_list *ero_subobj_list = dll_initialize();
+
+ /* Should return NULL if obj_list is empty */
+ struct pcep_message *message = pcep_msg_create_initiate(NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+
+ /* Should return NULL if obj_list does not have 2 entries */
+ dll_append(obj_list, srp);
+ message = pcep_msg_create_initiate(obj_list);
+ CU_ASSERT_PTR_NULL(message);
+
+ dll_append(obj_list, lsp);
+ dll_append(obj_list, ero);
+ message = pcep_msg_create_initiate(obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->msg_header);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 3);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + srp->header.encoded_object_length
+ + lsp->header.encoded_object_length
+ + ero->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
+
+void test_pcep_msg_create_notify(void)
+{
+ struct pcep_object_notify *notify_obj = pcep_obj_create_notify(
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED,
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST);
+
+ /* Should return NULL if the notify obj is empty */
+ struct pcep_message *message = pcep_msg_create_notify(NULL, NULL);
+ CU_ASSERT_PTR_NULL(message);
+
+ message = pcep_msg_create_notify(notify_obj, NULL);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + notify_obj->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+
+ struct pcep_object_rp *rp_obj =
+ pcep_obj_create_rp(0, false, false, false, false, 10, NULL);
+ double_linked_list *obj_list = dll_initialize();
+ dll_append(obj_list, rp_obj);
+ notify_obj = pcep_obj_create_notify(
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED,
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST);
+
+ message = pcep_msg_create_notify(notify_obj, obj_list);
+ CU_ASSERT_PTR_NOT_NULL(message);
+ pcep_encode_message(message, versioning);
+ CU_ASSERT_PTR_NOT_NULL(message->obj_list);
+ CU_ASSERT_EQUAL(message->obj_list->num_entries, 2);
+ CU_ASSERT_EQUAL(message->encoded_message_length,
+ MESSAGE_HEADER_LENGTH
+ + notify_obj->header.encoded_object_length
+ + rp_obj->header.encoded_object_length);
+ CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF);
+ CU_ASSERT_EQUAL(message->msg_header->pcep_version,
+ PCEP_MESSAGE_HEADER_VERSION);
+
+ pcep_msg_free_message(message);
+}
diff --git a/pceplib/test/pcep_msg_messages_test.h b/pceplib/test/pcep_msg_messages_test.h
new file mode 100644
index 0000000000..a3295c74eb
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_test.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_MSG_TEST_H_
+#define PCEP_MSG_MSG_TEST_H_
+
+/* functions to be tested from pcep-messages.c */
+int pcep_messages_test_suite_setup(void);
+int pcep_messages_test_suite_teardown(void);
+void pcep_messages_test_setup(void);
+void pcep_messages_test_teardown(void);
+void test_pcep_msg_create_open(void);
+void test_pcep_msg_create_request(void);
+void test_pcep_msg_create_request_svec(void);
+void test_pcep_msg_create_reply_nopath(void);
+void test_pcep_msg_create_reply(void);
+void test_pcep_msg_create_close(void);
+void test_pcep_msg_create_error(void);
+void test_pcep_msg_create_keepalive(void);
+void test_pcep_msg_create_report(void);
+void test_pcep_msg_create_update(void);
+void test_pcep_msg_create_initiate(void);
+void test_pcep_msg_create_notify(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_msg_messages_tests.c b/pceplib/test/pcep_msg_messages_tests.c
new file mode 100644
index 0000000000..bd85a16530
--- /dev/null
+++ b/pceplib/test/pcep_msg_messages_tests.c
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_msg_messages_test.h"
+#include "pcep_msg_tools_test.h"
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_object_error_types_test.h"
+#include "pcep_msg_tlvs_test.h"
+#include "pcep_msg_objects_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ CU_pSuite messages_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Messages Test Suite", pcep_messages_test_suite_setup,
+ pcep_messages_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_messages_test_setup, pcep_messages_test_teardown);
+ CU_add_test(messages_suite, "test_pcep_msg_create_open",
+ test_pcep_msg_create_open);
+ CU_add_test(messages_suite, "test_pcep_msg_create_request",
+ test_pcep_msg_create_request);
+ CU_add_test(messages_suite, "test_pcep_msg_create_request_svec",
+ test_pcep_msg_create_request_svec);
+ CU_add_test(messages_suite, "test_pcep_msg_create_reply_nopath",
+ test_pcep_msg_create_reply_nopath);
+ CU_add_test(messages_suite, "test_pcep_msg_create_reply",
+ test_pcep_msg_create_reply);
+ CU_add_test(messages_suite, "test_pcep_msg_create_close",
+ test_pcep_msg_create_close);
+ CU_add_test(messages_suite, "test_pcep_msg_create_error",
+ test_pcep_msg_create_error);
+ CU_add_test(messages_suite, "test_pcep_msg_create_keepalive",
+ test_pcep_msg_create_keepalive);
+ CU_add_test(messages_suite, "test_pcep_msg_create_report",
+ test_pcep_msg_create_report);
+ CU_add_test(messages_suite, "test_pcep_msg_create_update",
+ test_pcep_msg_create_update);
+ CU_add_test(messages_suite, "test_pcep_msg_create_initiate",
+ test_pcep_msg_create_initiate);
+ CU_add_test(messages_suite, "test_pcep_msg_create_notify",
+ test_pcep_msg_create_notify);
+
+ CU_pSuite tlvs_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP TLVs Test Suite", pcep_tlvs_test_suite_setup,
+ pcep_tlvs_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_tlvs_test_setup, pcep_tlvs_test_teardown);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_stateful_pce_capability",
+ test_pcep_tlv_create_stateful_pce_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_speaker_entity_id",
+ test_pcep_tlv_create_speaker_entity_id);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_db_version",
+ test_pcep_tlv_create_lsp_db_version);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_path_setup_type",
+ test_pcep_tlv_create_path_setup_type);
+ CU_add_test(tlvs_suite,
+ "test_pcep_tlv_create_path_setup_type_capability",
+ test_pcep_tlv_create_path_setup_type_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_sr_pce_capability",
+ test_pcep_tlv_create_sr_pce_capability);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_symbolic_path_name",
+ test_pcep_tlv_create_symbolic_path_name);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv4_lsp_identifiers",
+ test_pcep_tlv_create_ipv4_lsp_identifiers);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv6_lsp_identifiers",
+ test_pcep_tlv_create_ipv6_lsp_identifiers);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv4",
+ test_pcep_tlv_create_srpag_pol_id_ipv4);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv6",
+ test_pcep_tlv_create_srpag_pol_id_ipv6);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_name",
+ test_pcep_tlv_create_srpag_pol_name);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_id",
+ test_pcep_tlv_create_srpag_cp_id);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_pref",
+ test_pcep_tlv_create_srpag_cp_pref);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_error_code",
+ test_pcep_tlv_create_lsp_error_code);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv4_error_spec",
+ test_pcep_tlv_create_rsvp_ipv4_error_spec);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv6_error_spec",
+ test_pcep_tlv_create_rsvp_ipv6_error_spec);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_nopath_vector",
+ test_pcep_tlv_create_nopath_vector);
+ CU_add_test(tlvs_suite, "test_pcep_tlv_create_arbitrary",
+ test_pcep_tlv_create_arbitrary);
+
+ CU_pSuite objects_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Objects Test Suite", pcep_objects_test_suite_setup,
+ pcep_objects_test_suite_teardown, /* suite setup and cleanup
+ function pointers */
+ pcep_objects_test_setup, pcep_objects_test_teardown);
+ CU_add_test(objects_suite, "test_pcep_obj_create_open",
+ test_pcep_obj_create_open);
+ CU_add_test(objects_suite, "test_pcep_obj_create_open",
+ test_pcep_obj_create_open_with_tlvs);
+ CU_add_test(objects_suite, "test_pcep_obj_create_rp",
+ test_pcep_obj_create_rp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_nopath",
+ test_pcep_obj_create_nopath);
+ CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv4",
+ test_pcep_obj_create_endpoint_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv6",
+ test_pcep_obj_create_endpoint_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv4",
+ test_pcep_obj_create_association_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv6",
+ test_pcep_obj_create_association_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_bandwidth",
+ test_pcep_obj_create_bandwidth);
+ CU_add_test(objects_suite, "test_pcep_obj_create_metric",
+ test_pcep_obj_create_metric);
+ CU_add_test(objects_suite, "test_pcep_obj_create_lspa",
+ test_pcep_obj_create_lspa);
+ CU_add_test(objects_suite, "test_pcep_obj_create_svec",
+ test_pcep_obj_create_svec);
+ CU_add_test(objects_suite, "test_pcep_obj_create_error",
+ test_pcep_obj_create_error);
+ CU_add_test(objects_suite, "test_pcep_obj_create_close",
+ test_pcep_obj_create_close);
+ CU_add_test(objects_suite, "test_pcep_obj_create_srp",
+ test_pcep_obj_create_srp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_lsp",
+ test_pcep_obj_create_lsp);
+ CU_add_test(objects_suite, "test_pcep_obj_create_vendor_info",
+ test_pcep_obj_create_vendor_info);
+
+ CU_add_test(objects_suite, "test_pcep_obj_create_ero",
+ test_pcep_obj_create_ero);
+ CU_add_test(objects_suite, "test_pcep_obj_create_rro",
+ test_pcep_obj_create_rro);
+ CU_add_test(objects_suite, "test_pcep_obj_create_iro",
+ test_pcep_obj_create_iro);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv4",
+ test_pcep_obj_create_ro_subobj_ipv4);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv6",
+ test_pcep_obj_create_ro_subobj_ipv6);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_unnum",
+ test_pcep_obj_create_ro_subobj_unnum);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_32label",
+ test_pcep_obj_create_ro_subobj_32label);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_asn",
+ test_pcep_obj_create_ro_subobj_asn);
+
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_nonai",
+ test_pcep_obj_create_ro_subobj_sr_nonai);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_ipv4_node",
+ test_pcep_obj_create_ro_subobj_sr_ipv4_node);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_ipv6_node",
+ test_pcep_obj_create_ro_subobj_sr_ipv6_node);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv4_adj",
+ test_pcep_obj_create_ro_subobj_sr_ipv4_adj);
+ CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv6_adj",
+ test_pcep_obj_create_ro_subobj_sr_ipv6_adj);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj",
+ test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj);
+ CU_add_test(objects_suite,
+ "test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj",
+ test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj);
+
+ CU_pSuite tools_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Tools Test Suite", pcep_tools_test_suite_setup,
+ pcep_tools_test_suite_teardown, pcep_tools_test_setup,
+ pcep_tools_test_teardown);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate",
+ test_pcep_msg_read_pcep_initiate);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate2",
+ test_pcep_msg_read_pcep_initiate2);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update",
+ test_pcep_msg_read_pcep_update);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open",
+ test_pcep_msg_read_pcep_open);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_initiate",
+ test_pcep_msg_read_pcep_open_initiate);
+ CU_add_test(tools_suite, "test_validate_message_header",
+ test_validate_message_header);
+ CU_add_test(tools_suite, "test_validate_message_objects",
+ test_validate_message_objects);
+ CU_add_test(tools_suite, "test_validate_message_objects_invalid",
+ test_validate_message_objects_invalid);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_cisco_pce",
+ test_pcep_msg_read_pcep_open_cisco_pce);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update_cisco_pce",
+ test_pcep_msg_read_pcep_update_cisco_pce);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_report_cisco_pcc",
+ test_pcep_msg_read_pcep_report_cisco_pcc);
+ CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate_cisco_pcc",
+ test_pcep_msg_read_pcep_initiate_cisco_pcc);
+
+ CU_pSuite obj_errors_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Object Error Types Test Suite",
+ pcep_object_error_types_test_suite_setup,
+ pcep_object_error_types_test_suite_teardown,
+ pcep_object_error_types_test_setup,
+ pcep_object_error_types_test_teardown);
+ CU_add_test(obj_errors_suite, "test_get_error_type_str",
+ test_get_error_type_str);
+ CU_add_test(obj_errors_suite, "test_get_error_value_str",
+ test_get_error_value_str);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_msg_object_error_types_test.c b/pceplib/test/pcep_msg_object_error_types_test.c
new file mode 100644
index 0000000000..7275eaf098
--- /dev/null
+++ b/pceplib/test/pcep_msg_object_error_types_test.c
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_object_error_types.h"
+#include "pcep_msg_object_error_types_test.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+int pcep_object_error_types_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ set_logging_level(LOG_DEBUG);
+ return 0;
+}
+
+int pcep_object_error_types_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_object_error_types_test_setup(void)
+{
+}
+
+void pcep_object_error_types_test_teardown(void)
+{
+}
+
+void test_get_error_type_str()
+{
+ const char *error_type_str;
+ int i = 0;
+ for (; i < MAX_ERROR_TYPE; i++) {
+ error_type_str = get_error_type_str(i);
+ CU_ASSERT_PTR_NOT_NULL(error_type_str);
+ }
+
+ CU_ASSERT_PTR_NULL(get_error_type_str(-1));
+ CU_ASSERT_PTR_NULL(get_error_type_str(MAX_ERROR_TYPE));
+}
+
+void test_get_error_value_str()
+{
+ const char *error_value_str;
+ int i = 0, j = 0;
+
+ for (; i < MAX_ERROR_TYPE; i++) {
+ for (; j < MAX_ERROR_VALUE; j++) {
+ error_value_str = get_error_value_str(i, j);
+ CU_ASSERT_PTR_NOT_NULL(error_value_str);
+ }
+ }
+
+ CU_ASSERT_PTR_NULL(get_error_value_str(-1, 0));
+ CU_ASSERT_PTR_NULL(get_error_value_str(MAX_ERROR_TYPE, 0));
+ CU_ASSERT_PTR_NULL(get_error_value_str(1, -1));
+ CU_ASSERT_PTR_NULL(get_error_value_str(1, MAX_ERROR_VALUE));
+}
diff --git a/pceplib/test/pcep_msg_object_error_types_test.h b/pceplib/test/pcep_msg_object_error_types_test.h
new file mode 100644
index 0000000000..863517d1e3
--- /dev/null
+++ b/pceplib/test/pcep_msg_object_error_types_test.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_OBJECT_ERROR_TYPES_TEST_
+#define PCEP_MSG_OBJECT_ERROR_TYPES_TEST_
+
+int pcep_object_error_types_test_suite_setup(void);
+int pcep_object_error_types_test_suite_teardown(void);
+void pcep_object_error_types_test_setup(void);
+void pcep_object_error_types_test_teardown(void);
+void test_get_error_type_str(void);
+void test_get_error_value_str(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_msg_objects_test.c b/pceplib/test/pcep_msg_objects_test.c
new file mode 100644
index 0000000000..a4c069945c
--- /dev/null
+++ b/pceplib/test/pcep_msg_objects_test.c
@@ -0,0 +1,1289 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_objects_test.h"
+
+/*
+ * Notice:
+ * All of these object Unit Tests encode the created objects by explicitly
+ * calling pcep_encode_object() thus testing the object creation and the object
+ * encoding. All APIs expect IPs to be in network byte order.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+static uint8_t object_buf[2000];
+
+void reset_objects_buffer(void);
+
+int pcep_objects_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_objects_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void reset_objects_buffer()
+{
+ memset(object_buf, 0, 2000);
+}
+
+void pcep_objects_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+ reset_objects_buffer();
+}
+
+void pcep_objects_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+/* Internal util verification function */
+static void verify_pcep_obj_header2(uint8_t obj_class, uint8_t obj_type,
+ uint16_t obj_length, const uint8_t *obj_buf)
+{
+ /* Object Header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Object-Class | OT |Res|P|I| Object Length (bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ /* Not using CU_ASSERT_EQUAL here, so that in case of failure,
+ * we can provide more info in the error message. */
+ if (obj_buf[0] != obj_class) {
+ fprintf(stderr,
+ "Test failure obj_class expected [%d] found [%d]\n",
+ obj_class, obj_buf[0]);
+ CU_FAIL("Object Header Class");
+ }
+
+ uint8_t found8 = (obj_buf[1] >> 4) & 0x0f;
+ if (obj_type != found8) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] obj_type expected [%d] found [%d]\n",
+ obj_class, obj_type, found8);
+ CU_FAIL("Object Header Type");
+ }
+
+ uint8_t exp8 = 0;
+ found8 = obj_buf[1] & 0x0f;
+ if (exp8 != found8) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] flags expected [%d] found [%d]\n",
+ obj_class, exp8, found8);
+ CU_FAIL("Object Header Flags");
+ }
+
+ uint16_t found16 = ntohs(*((uint16_t *)(obj_buf + 2)));
+ if (obj_length != found16) {
+ fprintf(stderr,
+ "Test failure obj_class [%d] obj_length expected [%d] found [%d]\n",
+ obj_class, obj_length, found16);
+ CU_FAIL("Object Header Length");
+ }
+}
+
+/* Internal util verification function */
+static void verify_pcep_obj_header(uint8_t obj_class, uint8_t obj_type,
+ struct pcep_object_header *obj_hdr)
+{
+ verify_pcep_obj_header2(obj_class, obj_type,
+ pcep_object_get_length_by_hdr(obj_hdr),
+ obj_hdr->encoded_object);
+}
+
+void test_pcep_obj_create_open()
+{
+ uint8_t deadtimer = 60;
+ uint8_t keepalive = 30;
+ uint8_t sid = 1;
+
+ struct pcep_object_open *open =
+ pcep_obj_create_open(keepalive, deadtimer, sid, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(open);
+ pcep_encode_object(&open->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN,
+ &open->header);
+
+ CU_ASSERT_EQUAL(open->header.encoded_object[4],
+ (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive);
+ CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer);
+ CU_ASSERT_EQUAL(open->header.encoded_object[7], sid);
+
+ pcep_obj_free_object((struct pcep_object_header *)open);
+}
+
+void test_pcep_obj_create_open_with_tlvs()
+{
+ uint8_t deadtimer = 60;
+ uint8_t keepalive = 30;
+ uint8_t sid = 1;
+ double_linked_list *tlv_list = dll_initialize();
+
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ pcep_tlv_create_stateful_pce_capability(true, true, true, true,
+ true, true);
+ dll_append(tlv_list, tlv);
+ struct pcep_object_open *open =
+ pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list);
+
+ CU_ASSERT_PTR_NOT_NULL(open);
+ pcep_encode_object(&open->header, versioning, object_buf);
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN,
+ pcep_object_get_length_by_hdr(&open->header)
+ + sizeof(uint32_t) * 2,
+ open->header.encoded_object);
+ CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list);
+ CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 1);
+
+ CU_ASSERT_EQUAL(open->header.encoded_object[4],
+ (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0);
+ CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive);
+ CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer);
+ CU_ASSERT_EQUAL(open->header.encoded_object[7], sid);
+
+ pcep_obj_free_object((struct pcep_object_header *)open);
+}
+
+void test_pcep_obj_create_rp()
+{
+ uint32_t reqid = 15;
+ uint8_t invalid_priority = 100;
+ uint8_t priority = 7;
+
+ struct pcep_object_rp *rp = pcep_obj_create_rp(
+ invalid_priority, true, false, false, true, reqid, NULL);
+ CU_ASSERT_PTR_NULL(rp);
+
+ rp = pcep_obj_create_rp(priority, true, false, false, true, reqid,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(rp);
+ pcep_encode_object(&rp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP,
+ &rp->header);
+
+ CU_ASSERT_EQUAL(rp->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(rp->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(rp->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL((rp->header.encoded_object[7] & 0x07), priority);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_R);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_OF);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_B);
+ CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_O);
+ CU_ASSERT_EQUAL(*((uint32_t *)(rp->header.encoded_object + 8)),
+ htonl(reqid));
+
+ pcep_obj_free_object((struct pcep_object_header *)rp);
+}
+
+void test_pcep_obj_create_nopath()
+{
+ uint8_t ni = 8;
+ uint32_t errorcode = 42;
+
+ struct pcep_object_nopath *nopath =
+ pcep_obj_create_nopath(ni, true, errorcode);
+
+ CU_ASSERT_PTR_NOT_NULL(nopath);
+ pcep_encode_object(&nopath->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH,
+ &nopath->header);
+
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[4], ni);
+ CU_ASSERT_TRUE(nopath->header.encoded_object[5] & OBJECT_NOPATH_FLAG_C);
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(nopath->header.encoded_object[7], 0);
+
+ /* Verify the TLV */
+ CU_ASSERT_PTR_NOT_NULL(nopath->header.tlv_list);
+ struct pcep_object_tlv_nopath_vector *tlv =
+ (struct pcep_object_tlv_nopath_vector *)
+ nopath->header.tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(tlv->header.type, 1);
+ CU_ASSERT_EQUAL(tlv->error_code, errorcode);
+
+ CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 8)),
+ htons(PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR));
+ CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 10)),
+ htons(4));
+ CU_ASSERT_EQUAL(*((uint32_t *)(nopath->header.encoded_object + 12)),
+ htonl(errorcode));
+
+ pcep_obj_free_object((struct pcep_object_header *)nopath);
+}
+void test_pcep_obj_create_association_ipv4()
+{
+
+ uint16_t all_assoc_groups = 0xffff;
+ struct in_addr src;
+ inet_pton(AF_INET, "192.168.1.2", &src);
+
+ struct pcep_object_association_ipv4 *assoc =
+ pcep_obj_create_association_ipv4(
+ false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE,
+ all_assoc_groups, src);
+ CU_ASSERT_PTR_NOT_NULL(assoc);
+ CU_ASSERT_EQUAL(assoc->association_type,
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE);
+ CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups);
+ CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION);
+ CU_ASSERT_EQUAL(assoc->header.object_type,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV4);
+ CU_ASSERT_EQUAL(assoc->src.s_addr, src.s_addr);
+
+ pcep_obj_free_object((struct pcep_object_header *)assoc);
+}
+
+void test_pcep_obj_create_association_ipv6()
+{
+ uint32_t all_assoc_groups = 0xffff;
+ struct in6_addr src;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src);
+
+ struct pcep_object_association_ipv6 *assoc =
+ pcep_obj_create_association_ipv6(
+ false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE,
+ all_assoc_groups, src);
+ CU_ASSERT_PTR_NOT_NULL(assoc);
+ CU_ASSERT_EQUAL(assoc->association_type,
+ PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE);
+ CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups);
+ CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION);
+ CU_ASSERT_EQUAL(assoc->header.object_type,
+ PCEP_OBJ_TYPE_ASSOCIATION_IPV6);
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[0],
+ (src.__in6_u.__u6_addr32[0]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[1],
+ (src.__in6_u.__u6_addr32[1]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[2],
+ (src.__in6_u.__u6_addr32[2]));
+ CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[3],
+ (src.__in6_u.__u6_addr32[3]));
+
+ pcep_obj_free_object((struct pcep_object_header *)assoc);
+}
+
+void test_pcep_obj_create_endpoint_ipv4()
+{
+ struct in_addr src_ipv4, dst_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &src_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &dst_ipv4);
+
+ struct pcep_object_endpoints_ipv4 *ipv4 =
+ pcep_obj_create_endpoint_ipv4(NULL, NULL);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, NULL);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(NULL, &dst_ipv4);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ pcep_encode_object(&ipv4->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4, &ipv4->header);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 4)),
+ src_ipv4.s_addr);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 8)),
+ dst_ipv4.s_addr);
+
+ pcep_obj_free_object((struct pcep_object_header *)ipv4);
+}
+
+void test_pcep_obj_create_endpoint_ipv6()
+{
+ struct in6_addr src_ipv6, dst_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &dst_ipv6);
+
+ struct pcep_object_endpoints_ipv6 *ipv6 =
+ pcep_obj_create_endpoint_ipv6(NULL, NULL);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, NULL);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(NULL, &dst_ipv6);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, &dst_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ pcep_encode_object(&ipv6->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6, &ipv6->header);
+ uint32_t *uint32_ptr = (uint32_t *)(ipv6->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], src_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], src_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], src_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], src_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], dst_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], dst_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], dst_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], dst_ipv6.__in6_u.__u6_addr32[3]);
+
+ pcep_obj_free_object((struct pcep_object_header *)ipv6);
+}
+
+void test_pcep_obj_create_bandwidth()
+{
+ /* 1.8 => binary 1.11001101
+ * exponent = 127 => 0111 1111
+ * fraction = 1100 1101 0000 0000 0000 000 */
+ float bandwidth = 1.8;
+
+ struct pcep_object_bandwidth *bw = pcep_obj_create_bandwidth(bandwidth);
+
+ CU_ASSERT_PTR_NOT_NULL(bw);
+ pcep_encode_object(&bw->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_BANDWIDTH,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ, &bw->header);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[4], 0x3f);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[5], 0xe6);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[6], 0x66);
+ CU_ASSERT_EQUAL(bw->header.encoded_object[7], 0x66);
+
+ pcep_obj_free_object((struct pcep_object_header *)bw);
+}
+
+void test_pcep_obj_create_metric()
+{
+ uint8_t type = PCEP_METRIC_BORDER_NODE_COUNT;
+ /* https://en.wikipedia.org/wiki/IEEE_754-1985
+ * 0.15625 = 1/8 + 1/32 = binary 0.00101 = 1.01 x 10^-3
+ * Exponent bias = 127, so exponent = (127-3) = 124 = 0111 1100
+ * Sign Exponent Fraction
+ * (8 bits) (23 bits)
+ * 0.15625 => 0 0111 1100 010 0000 ... 0000 */
+ float value = 0.15625;
+
+ struct pcep_object_metric *metric =
+ pcep_obj_create_metric(type, true, true, value);
+
+ CU_ASSERT_PTR_NOT_NULL(metric);
+ pcep_encode_object(&metric->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC,
+ &metric->header);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[5], 0);
+ CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_B);
+ CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_C);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[7], type);
+ /* See comments above for explanation of these values */
+ CU_ASSERT_EQUAL(metric->header.encoded_object[8], 0x3e);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[9], 0x20);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[10], 0x00);
+ CU_ASSERT_EQUAL(metric->header.encoded_object[11], 0x00);
+
+ pcep_obj_free_object((struct pcep_object_header *)metric);
+}
+
+void test_pcep_obj_create_lspa()
+{
+ uint32_t exclude_any = 10;
+ uint32_t include_any = 20;
+ uint32_t include_all = 30;
+ uint8_t prio = 0;
+ uint8_t hold_prio = 10;
+
+ struct pcep_object_lspa *lspa = pcep_obj_create_lspa(
+ exclude_any, include_any, include_all, prio, hold_prio, true);
+
+ CU_ASSERT_PTR_NOT_NULL(lspa);
+ pcep_encode_object(&lspa->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA,
+ &lspa->header);
+ uint32_t *uint32_ptr = (uint32_t *)(lspa->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(exclude_any));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(include_any));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(include_all));
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[16], prio);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[17], hold_prio);
+ CU_ASSERT_TRUE(lspa->header.encoded_object[18] & OBJECT_LSPA_FLAG_L);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object[19], 0);
+
+ pcep_obj_free_object((struct pcep_object_header *)lspa);
+}
+
+void test_pcep_obj_create_svec()
+{
+ struct pcep_object_svec *svec =
+ pcep_obj_create_svec(true, true, true, NULL);
+ CU_ASSERT_PTR_NULL(svec);
+
+ double_linked_list *id_list = dll_initialize();
+ uint32_t *uint32_ptr =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *uint32_ptr = 10;
+ dll_append(id_list, uint32_ptr);
+
+ svec = pcep_obj_create_svec(true, true, true, id_list);
+ CU_ASSERT_PTR_NOT_NULL(svec);
+ pcep_encode_object(&svec->header, versioning, object_buf);
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_SVEC, PCEP_OBJ_TYPE_SVEC,
+ (OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2),
+ svec->header.encoded_object);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(svec->header.encoded_object[6], 0);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_S);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_N);
+ CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_L);
+ CU_ASSERT_EQUAL(*((uint32_t *)(svec->header.encoded_object + 8)),
+ htonl(*uint32_ptr));
+
+ pcep_obj_free_object((struct pcep_object_header *)svec);
+}
+
+void test_pcep_obj_create_error()
+{
+ uint8_t error_type = PCEP_ERRT_SESSION_FAILURE;
+ uint8_t error_value = PCEP_ERRV_RECVD_INVALID_OPEN_MSG;
+
+ struct pcep_object_error *error =
+ pcep_obj_create_error(error_type, error_value);
+
+ CU_ASSERT_PTR_NOT_NULL(error);
+ pcep_encode_object(&error->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR,
+ &error->header);
+ CU_ASSERT_EQUAL(error->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(error->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(error->header.encoded_object[6], error_type);
+ CU_ASSERT_EQUAL(error->header.encoded_object[7], error_value);
+
+ pcep_obj_free_object((struct pcep_object_header *)error);
+}
+
+void test_pcep_obj_create_close()
+{
+ uint8_t reason = PCEP_CLOSE_REASON_DEADTIMER;
+
+ struct pcep_object_close *close = pcep_obj_create_close(reason);
+
+ CU_ASSERT_PTR_NOT_NULL(close);
+ pcep_encode_object(&close->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_CLOSE, PCEP_OBJ_TYPE_CLOSE,
+ &close->header);
+ CU_ASSERT_EQUAL(close->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(close->header.encoded_object[7], reason);
+
+ pcep_obj_free_object((struct pcep_object_header *)close);
+}
+
+void test_pcep_obj_create_srp()
+{
+ bool lsp_remove = true;
+ uint32_t srp_id_number = 0x89674523;
+ struct pcep_object_srp *srp =
+ pcep_obj_create_srp(lsp_remove, srp_id_number, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(srp);
+ pcep_encode_object(&srp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP,
+ &srp->header);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[4], 0);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[5], 0);
+ CU_ASSERT_EQUAL(srp->header.encoded_object[6], 0);
+ CU_ASSERT_TRUE(srp->header.encoded_object[7] & OBJECT_SRP_FLAG_R);
+ CU_ASSERT_EQUAL(*((uint32_t *)(srp->header.encoded_object + 8)),
+ htonl(srp_id_number));
+
+ pcep_obj_free_object((struct pcep_object_header *)srp);
+}
+
+void test_pcep_obj_create_lsp()
+{
+ uint32_t plsp_id = 0x000fffff;
+ enum pcep_lsp_operational_status status = PCEP_LSP_OPERATIONAL_ACTIVE;
+ bool c_flag = true;
+ bool a_flag = true;
+ bool r_flag = true;
+ bool s_flag = true;
+ bool d_flag = true;
+
+ /* Should return for invalid plsp_id */
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(0x001fffff, status, c_flag, a_flag, r_flag,
+ s_flag, d_flag, NULL);
+ CU_ASSERT_PTR_NULL(lsp);
+
+ /* Should return for invalid status */
+ lsp = pcep_obj_create_lsp(plsp_id, 8, c_flag, a_flag, r_flag, s_flag,
+ d_flag, NULL);
+ CU_ASSERT_PTR_NULL(lsp);
+
+ lsp = pcep_obj_create_lsp(plsp_id, status, c_flag, a_flag, r_flag,
+ s_flag, d_flag, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL(lsp);
+ pcep_encode_object(&lsp->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP,
+ &lsp->header);
+ CU_ASSERT_EQUAL((ntohl(*((uint32_t *)(lsp->header.encoded_object + 4)))
+ >> 12) & 0x000fffff,
+ plsp_id);
+ CU_ASSERT_EQUAL((lsp->header.encoded_object[7] >> 4) & 0x07, status);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_A);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_C);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_D);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_R);
+ CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_S);
+
+ pcep_obj_free_object((struct pcep_object_header *)lsp);
+}
+
+void test_pcep_obj_create_vendor_info()
+{
+ uint32_t enterprise_number = 0x01020304;
+ uint32_t enterprise_specific_info = 0x05060708;
+
+ struct pcep_object_vendor_info *obj = pcep_obj_create_vendor_info(
+ enterprise_number, enterprise_specific_info);
+
+ CU_ASSERT_PTR_NOT_NULL(obj);
+ pcep_encode_object(&obj->header, versioning, object_buf);
+ verify_pcep_obj_header(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO, &obj->header);
+ uint32_t *uint32_ptr = (uint32_t *)(obj->header.encoded_object + 4);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(enterprise_number));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_specific_info));
+
+ pcep_obj_free_object((struct pcep_object_header *)obj);
+}
+
+/* Internal test function. The only difference between pcep_obj_create_ero(),
+ * pcep_obj_create_iro(), and pcep_obj_create_rro() is the object_class
+ * and the object_type.
+ */
+typedef struct pcep_object_ro *(*ro_func)(double_linked_list *);
+static void test_pcep_obj_create_object_common(ro_func func_to_test,
+ uint8_t object_class,
+ uint8_t object_type)
+{
+ double_linked_list *ero_list = dll_initialize();
+
+ struct pcep_object_ro *ero = func_to_test(NULL);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+
+ reset_objects_buffer();
+ ero = func_to_test(ero_list);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+
+ reset_objects_buffer();
+ struct pcep_ro_subobj_32label *ro_subobj =
+ pcep_obj_create_ro_subobj_32label(false, 0, 101);
+ ero_list = dll_initialize();
+ dll_append(ero_list, ro_subobj);
+ ero = func_to_test(ero_list);
+ CU_ASSERT_PTR_NOT_NULL(ero);
+ pcep_encode_object(&ero->header, versioning, object_buf);
+ /* 4 bytes for obj header +
+ * 2 bytes for ro_subobj header +
+ * 2 bytes for lable c-type and flags +
+ * 4 bytes for label */
+ verify_pcep_obj_header2(object_class, object_type,
+ OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2,
+ ero->header.encoded_object);
+ pcep_obj_free_object((struct pcep_object_header *)ero);
+}
+
+void test_pcep_obj_create_ero()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_ero, PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO);
+}
+
+void test_pcep_obj_create_rro()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_rro, PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO);
+}
+
+void test_pcep_obj_create_iro()
+{
+ test_pcep_obj_create_object_common(
+ pcep_obj_create_iro, PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO);
+}
+
+/* Internal util function to wrap an RO Subobj in a RO and encode it */
+static struct pcep_object_ro *encode_ro_subobj(struct pcep_object_ro_subobj *sr)
+{
+ double_linked_list *sr_subobj_list = dll_initialize();
+ dll_append(sr_subobj_list, sr);
+ struct pcep_object_ro *ro = pcep_obj_create_ero(sr_subobj_list);
+ pcep_encode_object(&ro->header, versioning, object_buf);
+
+ return ro;
+}
+
+static void verify_pcep_obj_ro_header(struct pcep_object_ro *ro,
+ struct pcep_object_ro_subobj *ro_subobj,
+ uint8_t ro_subobj_type, bool loose_hop,
+ uint16_t length)
+{
+ (void)ro_subobj;
+
+ verify_pcep_obj_header2(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO, length,
+ ro->header.encoded_object);
+
+ /* TODO consider printing the stack trace:
+ * https://stackoverflow.com/questions/105659/how-can-one-grab-a-stack-trace-in-c
+ */
+
+ /* Not using CU_ASSERT_EQUAL here, so that in case of failure,
+ * we can provide more info in the error message. */
+ uint8_t found_type = (ro->header.encoded_object[4]
+ & 0x7f); /* remove the Loose hop bit */
+ if (found_type != ro_subobj_type) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj_type expected [%d] found [%d]\n",
+ ro_subobj_type, found_type);
+ CU_FAIL("Sub Object Header Type");
+ }
+
+ bool loose_hop_found = (ro->header.encoded_object[4] & 0x80);
+ if (loose_hop != loose_hop_found) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj Loose Hop bit expected [%d] found [%d]\n",
+ loose_hop, loose_hop_found);
+ CU_FAIL("Sub Object Header Loose Hop bit");
+ }
+
+ if (length - 4 != ro->header.encoded_object[5]) {
+ fprintf(stderr,
+ "Test failure ro_sub_obj length expected [%d] found [%d]\n",
+ length - 4, ro->header.encoded_object[5]);
+ CU_FAIL("Sub Object Length");
+ }
+}
+
+static void
+verify_pcep_obj_ro_sr_header(struct pcep_object_ro *ro,
+ struct pcep_object_ro_subobj *ro_subobj,
+ uint8_t nai_type, bool loose_hop, uint16_t length)
+{
+ verify_pcep_obj_ro_header(ro, ro_subobj, RO_SUBOBJ_TYPE_SR, loose_hop,
+ length);
+ uint8_t found_nai_type = ((ro->header.encoded_object[6] >> 4) & 0x0f);
+ if (nai_type != found_nai_type) {
+ fprintf(stderr,
+ "Test failure ro_sr_sub_obj nai_type expected [%d] found [%d]\n",
+ nai_type, found_nai_type);
+ CU_FAIL("Sub Object SR NAI Type");
+ }
+}
+
+void test_pcep_obj_create_ro_subobj_ipv4()
+{
+ struct in_addr ro_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &ro_ipv4);
+ uint8_t prefix_len = 8;
+
+ struct pcep_ro_subobj_ipv4 *ipv4 =
+ pcep_obj_create_ro_subobj_ipv4(true, NULL, prefix_len, false);
+ CU_ASSERT_PTR_NULL(ipv4);
+
+ ipv4 = pcep_obj_create_ro_subobj_ipv4(false, &ro_ipv4, prefix_len,
+ true);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ struct pcep_object_ro *ro = encode_ro_subobj(&ipv4->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4,
+ false, sizeof(uint32_t) * 3);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)),
+ ro_ipv4.s_addr);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len);
+ CU_ASSERT_TRUE(ro->header.encoded_object[11]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ ipv4 = pcep_obj_create_ro_subobj_ipv4(true, &ro_ipv4, prefix_len,
+ false);
+ CU_ASSERT_PTR_NOT_NULL(ipv4);
+ ro = encode_ro_subobj(&ipv4->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4,
+ true, sizeof(uint32_t) * 3);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)),
+ ro_ipv4.s_addr);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[11], 0);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_ipv6()
+{
+ struct in6_addr ro_ipv6;
+ uint8_t prefix_len = 16;
+
+ struct pcep_ro_subobj_ipv6 *ipv6 =
+ pcep_obj_create_ro_subobj_ipv6(true, NULL, prefix_len, true);
+ CU_ASSERT_PTR_NULL(ipv6);
+
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ro_ipv6);
+ ipv6 = pcep_obj_create_ro_subobj_ipv6(false, &ro_ipv6, prefix_len,
+ true);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ struct pcep_object_ro *ro = encode_ro_subobj(&ipv6->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6,
+ false, sizeof(uint32_t) * 6);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len);
+ CU_ASSERT_TRUE(ro->header.encoded_object[23]
+ & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ ipv6 = pcep_obj_create_ro_subobj_ipv6(true, &ro_ipv6, prefix_len,
+ false);
+ CU_ASSERT_PTR_NOT_NULL(ipv6);
+ ro = encode_ro_subobj(&ipv6->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6,
+ true, sizeof(uint32_t) * 6);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[23], 0);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_unnum()
+{
+ struct in_addr router_id;
+ uint32_t if_id = 123;
+
+ struct pcep_ro_subobj_unnum *unnum =
+ pcep_obj_create_ro_subobj_unnum(NULL, if_id);
+ CU_ASSERT_PTR_NULL(unnum);
+
+ inet_pton(AF_INET, "192.168.1.2", &router_id);
+ unnum = pcep_obj_create_ro_subobj_unnum(&router_id, if_id);
+ CU_ASSERT_PTR_NOT_NULL(unnum);
+ struct pcep_object_ro *ro = encode_ro_subobj(&unnum->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &unnum->ro_subobj, RO_SUBOBJ_TYPE_UNNUM,
+ false, sizeof(uint32_t) * 4);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[6], 0);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ router_id.s_addr);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)),
+ htonl(if_id));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_32label()
+{
+ uint8_t class_type = 1;
+ uint32_t label = 0xeeffaabb;
+
+ struct pcep_ro_subobj_32label *label32 =
+ pcep_obj_create_ro_subobj_32label(true, class_type, label);
+ CU_ASSERT_PTR_NOT_NULL(label32);
+ struct pcep_object_ro *ro = encode_ro_subobj(&label32->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &label32->ro_subobj, RO_SUBOBJ_TYPE_LABEL,
+ false, sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[6]
+ & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL);
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], class_type);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ htonl(label));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_asn()
+{
+ uint16_t asn = 0x0102;
+
+ struct pcep_ro_subobj_asn *asn_obj = pcep_obj_create_ro_subobj_asn(asn);
+ CU_ASSERT_PTR_NOT_NULL(asn_obj);
+ struct pcep_object_ro *ro = encode_ro_subobj(&asn_obj->ro_subobj);
+ verify_pcep_obj_ro_header(ro, &asn_obj->ro_subobj, RO_SUBOBJ_TYPE_ASN,
+ false, sizeof(uint32_t) * 2);
+ CU_ASSERT_EQUAL(*((uint16_t *)(ro->header.encoded_object + 6)),
+ htons(asn));
+
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_nonai()
+{
+ uint32_t sid = 0x01020304;
+
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_nonai(false, sid, false, false);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_ABSENT, false,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_nonai(true, sid, true, true);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_ABSENT, true,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv4_node()
+{
+ uint32_t sid = 0x01020304;
+ struct in_addr ipv4_node_id;
+ inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv4_node_id) */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_node(
+ true, false, true, true, sid, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_node(true, true, false, false,
+ sid, &ipv4_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, true,
+ sizeof(uint32_t) * 3);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ ipv4_node_id.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id);
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_node(false, false, true, true,
+ sid, &ipv4_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE, false,
+ sizeof(uint32_t) * 4);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)),
+ htonl(sid));
+ CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)),
+ ipv4_node_id.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv6_node()
+{
+ uint32_t sid = 0x01020304;
+ struct in6_addr ipv6_node_id;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv6_node_id) */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_node(
+ false, true, true, true, sid, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_node(true, true, true, true, sid,
+ &ipv6_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, true,
+ sizeof(uint32_t) * 6);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], ipv6_node_id.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id);
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_node(false, false, true, true,
+ sid, &ipv6_node_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_NODE, false,
+ sizeof(uint32_t) * 7);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], ipv6_node_id.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv4_adj()
+{
+ struct in_addr local_ipv4;
+ struct in_addr remote_ipv4;
+ inet_pton(AF_INET, "192.168.1.2", &local_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &remote_ipv4);
+
+ uint32_t sid = ENCODE_SR_ERO_SID(3, 7, 0, 188);
+ CU_ASSERT_EQUAL(sid, 16060);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv4, remote_ipv4)
+ */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ false, true, true, true, sid, NULL, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid,
+ &local_ipv4, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid,
+ NULL, &remote_ipv4);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(true, true, true, true, sid,
+ &local_ipv4, &remote_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, true,
+ sizeof(uint32_t) * 4);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv4.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[1], remote_ipv4.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ inet_pton(AF_INET, "192.168.1.2", &local_ipv4);
+ inet_pton(AF_INET, "172.168.1.2", &remote_ipv4);
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ false, false, true, true, sid, &local_ipv4, &remote_ipv4);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, false,
+ sizeof(uint32_t) * 5);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv4.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2], remote_ipv4.s_addr);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_ipv6_adj()
+{
+ uint32_t sid = 0x01020304;
+ struct in6_addr local_ipv6;
+ struct in6_addr remote_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, remote_ipv6)
+ */
+ struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ false, true, true, true, sid, NULL, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid,
+ &local_ipv6, NULL);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid,
+ NULL, &remote_ipv6);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(true, true, true, true, sid,
+ &local_ipv6, &remote_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, true,
+ sizeof(uint32_t) * 10);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]);
+
+ CU_ASSERT_EQUAL(uint32_ptr[4], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+ sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ false, false, true, false, sid, &local_ipv6, &remote_ipv6);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, false,
+ sizeof(uint32_t) * 11);
+ /* All flags are false */
+ CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]);
+
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
+
+void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj()
+{
+ uint32_t sid = 0x01020304;
+ uint32_t local_node_id = 0x11223344;
+ uint32_t local_if_id = 0x55667788;
+ uint32_t remote_node_id = 0x99aabbcc;
+ uint32_t remote_if_id = 0xddeeff11;
+
+ /* (loose_hop, sid_absent, c_flag, m_flag,
+ sid, local_node_id, local_if_id, remote_node_id, remote_if_id) */
+
+ /* Test the sid is absent */
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ true, true, true, true, sid, local_node_id, local_if_id,
+ remote_node_id, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, true,
+ sizeof(uint32_t) * 6);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_if_id);
+ CU_ASSERT_EQUAL(uint32_ptr[2], remote_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[3], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ false, false, true, true, sid, local_node_id, local_if_id,
+ remote_node_id, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, false,
+ sizeof(uint32_t) * 7);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_if_id);
+ CU_ASSERT_EQUAL(uint32_ptr[3], remote_node_id);
+ CU_ASSERT_EQUAL(uint32_ptr[4], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* TODO Test draft07 types */
+}
+
+void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj()
+{
+ uint32_t sid = 0x01020304;
+ uint32_t local_if_id = 0x11002200;
+ uint32_t remote_if_id = 0x00110022;
+ struct in6_addr local_ipv6;
+ struct in6_addr remote_ipv6;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+
+ /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, local_if_id,
+ * remote_ipv6, remote_if_id */
+ struct pcep_ro_subobj_sr *sr =
+ pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, NULL, local_if_id, NULL,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, &local_ipv6, local_if_id, NULL,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, true, true, true, sid, NULL, local_if_id, &remote_ipv6,
+ remote_if_id);
+ CU_ASSERT_PTR_NULL(sr);
+
+ /* Test the sid is absent */
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ true, true, true, true, sid, &local_ipv6, local_if_id,
+ &remote_ipv6, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, true,
+ sizeof(uint32_t) * 12);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_EQUAL(sr->sid, 0);
+ uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_if_id);
+
+ CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[9], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+
+ /* Test the sid is present */
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6);
+ reset_objects_buffer();
+ sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(
+ false, false, true, true, sid, &local_ipv6, local_if_id,
+ &remote_ipv6, remote_if_id);
+ CU_ASSERT_PTR_NOT_NULL(sr);
+ ro = encode_ro_subobj(&sr->ro_subobj);
+ verify_pcep_obj_ro_sr_header(
+ ro, &sr->ro_subobj,
+ PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, false,
+ sizeof(uint32_t) * 13);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S);
+ CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F);
+ uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8);
+ CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid));
+ CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[5], local_if_id);
+
+ CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[0]);
+ CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[1]);
+ CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[2]);
+ CU_ASSERT_EQUAL(uint32_ptr[9], remote_ipv6.__in6_u.__u6_addr32[3]);
+ CU_ASSERT_EQUAL(uint32_ptr[10], remote_if_id);
+ pcep_obj_free_object((struct pcep_object_header *)ro);
+}
diff --git a/pceplib/test/pcep_msg_objects_test.h b/pceplib/test/pcep_msg_objects_test.h
new file mode 100644
index 0000000000..0f08193a59
--- /dev/null
+++ b/pceplib/test/pcep_msg_objects_test.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+
+#ifndef PCEP_MSG_OBJECTS_TEST_H_
+#define PCEP_MSG_OBJECTS_TEST_H_
+
+int pcep_objects_test_suite_setup(void);
+int pcep_objects_test_suite_teardown(void);
+void pcep_objects_test_setup(void);
+void pcep_objects_test_teardown(void);
+void test_pcep_obj_create_open(void);
+void test_pcep_obj_create_open_with_tlvs(void);
+void test_pcep_obj_create_rp(void);
+void test_pcep_obj_create_nopath(void);
+void test_pcep_obj_create_endpoint_ipv4(void);
+void test_pcep_obj_create_endpoint_ipv6(void);
+void test_pcep_obj_create_association_ipv4(void);
+void test_pcep_obj_create_association_ipv6(void);
+void test_pcep_obj_create_bandwidth(void);
+void test_pcep_obj_create_metric(void);
+void test_pcep_obj_create_lspa(void);
+void test_pcep_obj_create_svec(void);
+void test_pcep_obj_create_error(void);
+void test_pcep_obj_create_close(void);
+void test_pcep_obj_create_srp(void);
+void test_pcep_obj_create_lsp(void);
+void test_pcep_obj_create_vendor_info(void);
+void test_pcep_obj_create_ero(void);
+void test_pcep_obj_create_rro(void);
+void test_pcep_obj_create_iro(void);
+void test_pcep_obj_create_ro_subobj_ipv4(void);
+void test_pcep_obj_create_ro_subobj_ipv6(void);
+void test_pcep_obj_create_ro_subobj_unnum(void);
+void test_pcep_obj_create_ro_subobj_32label(void);
+void test_pcep_obj_create_ro_subobj_asn(void);
+void test_pcep_obj_create_ro_subobj_sr_nonai(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv4_node(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv6_node(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv4_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_ipv6_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(void);
+void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(void);
+
+#endif
diff --git a/pceplib/test/pcep_msg_tests_valgrind.sh b/pceplib/test/pcep_msg_tests_valgrind.sh
new file mode 100755
index 0000000000..4a9a99939d
--- /dev/null
+++ b/pceplib/test/pcep_msg_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_msg_tests
diff --git a/pceplib/test/pcep_msg_tlvs_test.c b/pceplib/test/pcep_msg_tlvs_test.c
new file mode 100644
index 0000000000..878e4d62af
--- /dev/null
+++ b/pceplib/test/pcep_msg_tlvs_test.c
@@ -0,0 +1,671 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tlvs.h"
+#include "pcep_msg_tools.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_tlvs_test.h"
+
+/*
+ * Notice:
+ * All of these TLV Unit Tests encode the created TLVs by explicitly calling
+ * pcep_encode_tlv() thus testing the TLV creation and the TLV encoding.
+ * All APIs expect IPs to be in network byte order.
+ */
+
+static struct pcep_versioning *versioning = NULL;
+static uint8_t tlv_buf[2000];
+
+void reset_tlv_buffer(void);
+
+int pcep_tlvs_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_tlvs_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void reset_tlv_buffer()
+{
+ memset(tlv_buf, 0, 2000);
+}
+
+void pcep_tlvs_test_setup()
+{
+ versioning = create_default_pcep_versioning();
+ reset_tlv_buffer();
+}
+
+void pcep_tlvs_test_teardown()
+{
+ destroy_pcep_versioning(versioning);
+}
+
+void test_pcep_tlv_create_stateful_pce_capability()
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv =
+ pcep_tlv_create_stateful_pce_capability(true, true, true, true,
+ true, true);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_TRUE(tlv->flag_u_lsp_update_capability);
+ CU_ASSERT_TRUE(tlv->flag_s_include_db_version);
+ CU_ASSERT_TRUE(tlv->flag_i_lsp_instantiation_capability);
+ CU_ASSERT_TRUE(tlv->flag_t_triggered_resync);
+ CU_ASSERT_TRUE(tlv->flag_d_delta_lsp_sync);
+ CU_ASSERT_TRUE(tlv->flag_f_triggered_initial_sync);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0x3f);
+ /* TODO add a new function: verify_tlv_header(tlv->header.encoded_tlv)
+ * to all tests */
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_speaker_entity_id()
+{
+ struct pcep_object_tlv_speaker_entity_identifier *tlv =
+ pcep_tlv_create_speaker_entity_id(NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ double_linked_list *list = dll_initialize();
+ tlv = pcep_tlv_create_speaker_entity_id(list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ uint32_t *speaker_entity =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t));
+ *speaker_entity = 42;
+ dll_append(list, speaker_entity);
+ tlv = pcep_tlv_create_speaker_entity_id(list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_PTR_NOT_NULL(tlv->speaker_entity_id_list);
+ CU_ASSERT_EQUAL(tlv->speaker_entity_id_list->num_entries, 1);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(*speaker_entity));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_lsp_db_version()
+{
+ uint64_t lsp_db_version = 0xf005ba11ba5eba11;
+ struct pcep_object_tlv_lsp_db_version *tlv =
+ pcep_tlv_create_lsp_db_version(lsp_db_version);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint64_t));
+ CU_ASSERT_EQUAL(tlv->lsp_db_version, lsp_db_version);
+ CU_ASSERT_EQUAL(*((uint64_t *)(tlv->header.encoded_tlv + 4)),
+ be64toh(lsp_db_version));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_path_setup_type()
+{
+ uint8_t pst = 0x89;
+
+ struct pcep_object_tlv_path_setup_type *tlv =
+ pcep_tlv_create_path_setup_type(pst);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ CU_ASSERT_EQUAL(tlv->path_setup_type, pst);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x000000FF & pst));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_path_setup_type_capability()
+{
+ /* The sub_tlv list is optional */
+
+ /* Should return NULL if pst_list is NULL */
+ struct pcep_object_tlv_path_setup_type_capability *tlv =
+ pcep_tlv_create_path_setup_type_capability(NULL, NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should return NULL if pst_list is empty */
+ double_linked_list *pst_list = dll_initialize();
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list, NULL);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should still return NULL if pst_list is NULL */
+ double_linked_list *sub_tlv_list = dll_initialize();
+ tlv = pcep_tlv_create_path_setup_type_capability(NULL, sub_tlv_list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Should still return NULL if pst_list is empty */
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ /* Test only populating the pst list */
+ uint8_t *pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ uint8_t *pst2 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ uint8_t *pst3 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ *pst1 = 1;
+ *pst2 = 2;
+ *pst3 = 3;
+ dll_append(pst_list, pst1);
+ dll_append(pst_list, pst2);
+ dll_append(pst_list, pst3);
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 2);
+ CU_ASSERT_PTR_NOT_NULL(tlv->pst_list);
+ CU_ASSERT_EQUAL(tlv->pst_list->num_entries, 3);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000003));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01020300));
+ pcep_obj_free_tlv(&tlv->header);
+
+ /* Now test populating both the pst_list and the sub_tlv_list */
+ reset_tlv_buffer();
+ struct pcep_object_tlv_header *sub_tlv =
+ (struct pcep_object_tlv_header *)
+ pcep_tlv_create_sr_pce_capability(true, true, 0);
+ pst_list = dll_initialize();
+ sub_tlv_list = dll_initialize();
+ pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1);
+ *pst1 = 1;
+ dll_append(pst_list, pst1);
+ dll_append(sub_tlv_list, sub_tlv);
+ tlv = pcep_tlv_create_path_setup_type_capability(pst_list,
+ sub_tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ sizeof(uint32_t) * 2 + TLV_HEADER_LENGTH
+ + sub_tlv->encoded_tlv_length);
+ CU_ASSERT_PTR_NOT_NULL(tlv->pst_list);
+ CU_ASSERT_PTR_NOT_NULL(tlv->sub_tlv_list);
+ uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length));
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000001));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01000000));
+ /* Verify the Sub-TLV */
+ uint16_ptr = (uint16_t *)(tlv->header.encoded_tlv + 12);
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(4));
+ CU_ASSERT_EQUAL(uint16_ptr[2], 0);
+ CU_ASSERT_EQUAL(uint16_ptr[3], htons(0x0300));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_sr_pce_capability()
+{
+ struct pcep_object_tlv_sr_pce_capability *tlv =
+ pcep_tlv_create_sr_pce_capability(true, true, 8);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint16_ptr[0],
+ htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY));
+ CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length));
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000308));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_symbolic_path_name()
+{
+ /* char *symbolic_path_name, uint16_t symbolic_path_name_length); */
+ char path_name[16] = "Some Path Name";
+ uint16_t path_name_length = 14;
+ struct pcep_object_tlv_symbolic_path_name *tlv =
+ pcep_tlv_create_symbolic_path_name(path_name, path_name_length);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, path_name_length);
+ /* Test the padding is correct */
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[4]),
+ &path_name[0], 4));
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[8]),
+ &path_name[4], 4));
+ CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[12]),
+ &path_name[8], 4));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[16], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[17], 'e');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[18], 0);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[19], 0);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_symbolic_path_name(path_name, 3);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_ipv4_lsp_identifiers()
+{
+ struct in_addr sender_ip, endpoint_ip;
+ uint16_t lsp_id = 7;
+ uint16_t tunnel_id = 16;
+ struct in_addr extended_tunnel_id;
+ extended_tunnel_id.s_addr = 256;
+ inet_pton(AF_INET, "192.168.1.1", &sender_ip);
+ inet_pton(AF_INET, "192.168.1.2", &endpoint_ip);
+
+ struct pcep_object_tlv_ipv4_lsp_identifier *tlv =
+ pcep_tlv_create_ipv4_lsp_identifiers(NULL, &endpoint_ip, lsp_id,
+ tunnel_id,
+ &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(
+ &sender_ip, NULL, lsp_id, tunnel_id, &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(
+ NULL, NULL, lsp_id, tunnel_id, &extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip,
+ lsp_id, tunnel_id,
+ &extended_tunnel_id);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+ CU_ASSERT_EQUAL(uint32_ptr[3], extended_tunnel_id.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip,
+ lsp_id, tunnel_id, NULL);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4);
+ uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr);
+ CU_ASSERT_EQUAL(uint32_ptr[2],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+ CU_ASSERT_EQUAL(uint32_ptr[3], INADDR_ANY);
+ CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_ipv6_lsp_identifiers()
+{
+ struct in6_addr sender_ip, endpoint_ip;
+ uint16_t lsp_id = 3;
+ uint16_t tunnel_id = 16;
+ uint32_t extended_tunnel_id[4];
+
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &sender_ip);
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &endpoint_ip);
+ extended_tunnel_id[0] = 1;
+ extended_tunnel_id[1] = 2;
+ extended_tunnel_id[2] = 3;
+ extended_tunnel_id[3] = 4;
+
+ struct pcep_object_tlv_ipv6_lsp_identifier *tlv =
+ pcep_tlv_create_ipv6_lsp_identifiers(
+ NULL, &endpoint_ip, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ &sender_ip, NULL, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ NULL, NULL, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_ipv6_lsp_identifiers(
+ &sender_ip, &endpoint_ip, lsp_id, tunnel_id,
+ (struct in6_addr *)&extended_tunnel_id);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 52);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[5],
+ (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_id_ipv4()
+{
+ uint32_t color = 1;
+ struct in_addr src;
+ inet_pton(AF_INET, "192.168.1.2", &src);
+
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ pcep_tlv_create_srpag_pol_id_ipv4(color, (void *)&src);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID));
+ CU_ASSERT_EQUAL(
+ tlv->header.encoded_tlv_length,
+ (8 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/));
+ CU_ASSERT_EQUAL(tlv->color, (color));
+ uint32_t aux_color = htonl(color); // Is color right encoded
+ CU_ASSERT_EQUAL(0, memcmp(&tlv_buf[0] + TLV_HEADER_LENGTH, &aux_color,
+ sizeof(color)));
+ CU_ASSERT_EQUAL(tlv->end_point.ipv4.s_addr, (src.s_addr));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_pol_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->color, dec_tlv->color);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_id_ipv6()
+{
+
+ uint32_t color = 1;
+ struct in6_addr src;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src);
+
+ struct pcep_object_tlv_srpag_pol_id *tlv =
+ pcep_tlv_create_srpag_pol_id_ipv6(color, &src);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID));
+ CU_ASSERT_EQUAL(
+ tlv->header.encoded_tlv_length,
+ (20 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/));
+ CU_ASSERT_EQUAL(tlv->color, (color));
+ CU_ASSERT_EQUAL(0, memcmp(&tlv->end_point.ipv6, &src, sizeof(src)));
+
+ uint32_t aux_color = htonl(color);
+ CU_ASSERT_EQUAL(0, memcmp(&aux_color, tlv_buf + TLV_HEADER_LENGTH,
+ sizeof(tlv->color)));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_pol_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_pol_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->color, dec_tlv->color);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_srpag_pol_name()
+{
+ const char *pol_name = "Some Pol Name";
+
+ struct pcep_object_tlv_srpag_pol_name *tlv =
+ pcep_tlv_create_srpag_pol_name(pol_name, strlen(pol_name));
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ (normalize_pcep_tlv_length(strlen(pol_name))));
+ CU_ASSERT_EQUAL(0, strcmp(pol_name, (char *)tlv->name));
+
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_srpag_cp_id()
+{
+ // draft-ietf-spring-segment-routing-policy-06.pdf#2.3
+ // 10 PCEP, 20 BGP SR Policy, 30 Via Configuration
+ uint8_t proto_origin = 10;
+ uint32_t ASN = 0;
+ struct in6_addr with_mapped_ipv4;
+ inet_pton(AF_INET6, "::ffff:192.0.2.128", &with_mapped_ipv4);
+ uint32_t discriminator = 0;
+
+ struct pcep_object_tlv_srpag_cp_id *tlv = pcep_tlv_create_srpag_cp_id(
+ proto_origin, ASN, &with_mapped_ipv4, discriminator);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ (sizeof(proto_origin) + sizeof(ASN)
+ + sizeof(with_mapped_ipv4) + sizeof(discriminator)));
+ CU_ASSERT_EQUAL(tlv->proto, (proto_origin));
+ CU_ASSERT_EQUAL(tlv->orig_asn, (ASN));
+ CU_ASSERT_EQUAL(0, memcmp(&tlv->orig_addres, &with_mapped_ipv4,
+ sizeof(with_mapped_ipv4)));
+ CU_ASSERT_EQUAL(tlv->discriminator, (discriminator));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_cp_id *dec_tlv =
+ (struct pcep_object_tlv_srpag_cp_id *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->proto, dec_tlv->proto);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_srpag_cp_pref()
+{
+ uint32_t preference_default = 100;
+
+ struct pcep_object_tlv_srpag_cp_pref *tlv =
+ pcep_tlv_create_srpag_cp_pref(preference_default);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type,
+ (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE));
+ printf(" encoded length vs sizeof pref (%d) vs (%ld)\n",
+ tlv->header.encoded_tlv_length, sizeof(preference_default));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length,
+ sizeof(preference_default));
+ CU_ASSERT_EQUAL(tlv->preference, (preference_default));
+ uint32_t aux_pref = htonl(preference_default); // Is pref right encoded
+ CU_ASSERT_EQUAL(0, memcmp(tlv_buf + TLV_HEADER_LENGTH, &aux_pref,
+ sizeof(preference_default)));
+ // Are simetrical?
+ struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf);
+ struct pcep_object_tlv_srpag_cp_pref *dec_tlv =
+ (struct pcep_object_tlv_srpag_cp_pref *)dec_hdr;
+ CU_ASSERT_EQUAL(tlv->preference, dec_tlv->preference);
+
+ pceplib_free(PCEPLIB_MESSAGES, dec_hdr);
+ pcep_obj_free_tlv(&tlv->header);
+}
+void test_pcep_tlv_create_lsp_error_code()
+{
+ struct pcep_object_tlv_lsp_error_code *tlv =
+ pcep_tlv_create_lsp_error_code(
+ PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t));
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1],
+ htonl(PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_rsvp_ipv4_error_spec()
+{
+ struct in_addr error_node_ip;
+ inet_pton(AF_INET, "192.168.1.1", &error_node_ip);
+ uint8_t error_code = 8;
+ uint16_t error_value = 0xaabb;
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ pcep_tlv_create_rsvp_ipv4_error_spec(NULL, error_code,
+ error_value);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_rsvp_ipv4_error_spec(&error_node_ip, error_code,
+ error_value);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 12);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_rsvp_ipv6_error_spec()
+{
+ struct in6_addr error_node_ip;
+ inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &error_node_ip);
+ uint8_t error_code = 8;
+ uint16_t error_value = 0xaabb;
+
+ struct pcep_object_tlv_rsvp_error_spec *tlv =
+ pcep_tlv_create_rsvp_ipv6_error_spec(NULL, error_code,
+ error_value);
+ CU_ASSERT_PTR_NULL(tlv);
+
+ tlv = pcep_tlv_create_rsvp_ipv6_error_spec(&error_node_ip, error_code,
+ error_value);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 24);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_nopath_vector()
+{
+ uint32_t enterprise_number = 0x01020304;
+ uint32_t enterprise_specific_info = 0x05060708;
+
+ struct pcep_object_tlv_vendor_info *tlv = pcep_tlv_create_vendor_info(
+ enterprise_number, enterprise_specific_info);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 8);
+ uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv;
+ CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_number));
+ CU_ASSERT_EQUAL(uint32_ptr[2], htonl(enterprise_specific_info));
+
+ pcep_obj_free_tlv(&tlv->header);
+}
+
+void test_pcep_tlv_create_arbitrary()
+{
+ char data[16] = "Some Data";
+ uint16_t data_length = 9;
+ uint16_t tlv_id_unknown = 1; // 65505; // Whatever id to be created
+ struct pcep_object_tlv_arbitrary *tlv = pcep_tlv_create_tlv_arbitrary(
+ data, data_length, tlv_id_unknown);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, data_length);
+ /* Test the padding is correct */
+ CU_ASSERT_EQUAL(
+ 0, strncmp((char *)&(tlv->header.encoded_tlv[4]), &data[0], 4));
+ CU_ASSERT_EQUAL(
+ 0, strncmp((char *)&(tlv->header.encoded_tlv[8]), &data[4], 4));
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[11], 't');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[12], 'a');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[13], 0);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[14], 0);
+ pcep_obj_free_tlv(&tlv->header);
+
+ reset_tlv_buffer();
+ tlv = pcep_tlv_create_tlv_arbitrary(data, 3, tlv_id_unknown);
+ CU_ASSERT_PTR_NOT_NULL(tlv);
+ pcep_encode_tlv(&tlv->header, versioning, tlv_buf);
+ CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3);
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm');
+ CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0);
+
+ pcep_obj_free_tlv(&tlv->header);
+}
diff --git a/pceplib/test/pcep_msg_tlvs_test.h b/pceplib/test/pcep_msg_tlvs_test.h
new file mode 100644
index 0000000000..a961d7e473
--- /dev/null
+++ b/pceplib/test/pcep_msg_tlvs_test.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+#ifndef PCEP_MSG_TLVS_TEST_H_
+#define PCEP_MSG_TLVS_TEST_H_
+
+int pcep_tlvs_test_suite_setup(void);
+int pcep_tlvs_test_suite_teardown(void);
+void pcep_tlvs_test_setup(void);
+void pcep_tlvs_test_teardown(void);
+void test_pcep_tlv_create_stateful_pce_capability(void);
+void test_pcep_tlv_create_speaker_entity_id(void);
+void test_pcep_tlv_create_lsp_db_version(void);
+void test_pcep_tlv_create_path_setup_type(void);
+void test_pcep_tlv_create_path_setup_type_capability(void);
+void test_pcep_tlv_create_sr_pce_capability(void);
+void test_pcep_tlv_create_symbolic_path_name(void);
+void test_pcep_tlv_create_ipv4_lsp_identifiers(void);
+void test_pcep_tlv_create_ipv6_lsp_identifiers(void);
+void test_pcep_tlv_create_lsp_error_code(void);
+void test_pcep_tlv_create_rsvp_ipv4_error_spec(void);
+void test_pcep_tlv_create_rsvp_ipv6_error_spec(void);
+void test_pcep_tlv_create_srpag_pol_id_ipv4(void);
+void test_pcep_tlv_create_srpag_pol_id_ipv6(void);
+void test_pcep_tlv_create_srpag_pol_name(void);
+void test_pcep_tlv_create_srpag_cp_id(void);
+void test_pcep_tlv_create_srpag_cp_pref(void);
+void test_pcep_tlv_create_nopath_vector(void);
+void test_pcep_tlv_create_arbitrary(void);
+
+
+#endif
diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c
new file mode 100644
index 0000000000..a1260b1186
--- /dev/null
+++ b/pceplib/test/pcep_msg_tools_test.c
@@ -0,0 +1,1258 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_msg_messages.h"
+#include "pcep_msg_tools.h"
+#include "pcep_msg_tools_test.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+const uint8_t any_obj_class = 255;
+
+uint16_t pcep_open_hexbyte_strs_length = 28;
+const char *pcep_open_odl_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e",
+ "78", "55", "00", "10", "00", "04", "00", "00", "00", "3f",
+ "00", "1a", "00", "04", "00", "00", "00", "00"};
+
+/* PCEP INITIATE str received from ODL with 4 objects: [SRP, LSP, Endpoints,
+ * ERO] The LSP has a SYMBOLIC_PATH_NAME TLV. The ERO has 2 IPV4 Endpoints. */
+uint16_t pcep_initiate_hexbyte_strs_length = 68;
+const char *pcep_initiate_hexbyte_strs[] = {
+ "20", "0c", "00", "44", "21", "12", "00", "0c", "00", "00", "00", "00",
+ "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09",
+ "00", "11", "00", "08", "66", "61", "39", "33", "33", "39", "32", "39",
+ "04", "10", "00", "0c", "7f", "00", "00", "01", "28", "28", "28", "28",
+ "07", "10", "00", "14", "01", "08", "0a", "00", "01", "01", "18", "00",
+ "01", "08", "0a", "00", "07", "04", "18", "00"};
+
+uint16_t pcep_initiate2_hexbyte_strs_length = 72;
+const char *pcep_initiate2_hexbyte_strs[] = {
+ "20", "0c", "00", "48", "21", "12", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "14", "00", "00", "00", "09", "00", "11", "00", "08",
+ "36", "65", "31", "31", "38", "39", "32", "31", "04", "10", "00", "0c",
+ "c0", "a8", "14", "05", "01", "01", "01", "01", "07", "10", "00", "10",
+ "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"};
+
+uint16_t pcep_update_hexbyte_strs_length = 48;
+const char *pcep_update_hexbyte_strs[] = {
+ "20", "0b", "00", "30", "21", "12", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "08", "00", "02", "a0", "09", "07", "10", "00", "10",
+ "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"};
+
+/* Test that pcep_msg_read() can read multiple messages in 1 call */
+uint16_t pcep_open_initiate_hexbyte_strs_length = 100;
+const char *pcep_open_initiate_odl_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e", "78", "55",
+ "00", "10", "00", "04", "00", "00", "00", "3f", "00", "1a", "00", "04",
+ "00", "00", "00", "00", "20", "0c", "00", "48", "21", "12", "00", "14",
+ "00", "00", "00", "00", "00", "00", "00", "01", "00", "1c", "00", "04",
+ "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09",
+ "00", "11", "00", "08", "36", "65", "31", "31", "38", "39", "32", "31",
+ "04", "10", "00", "0c", "c0", "a8", "14", "05", "01", "01", "01", "01",
+ "07", "10", "00", "10", "05", "0c", "10", "01", "03", "e8", "a0", "00",
+ "01", "01", "01", "01"};
+
+uint16_t pcep_open_cisco_pce_hexbyte_strs_length = 28;
+const char *pcep_open_cisco_pce_hexbyte_strs[] = {
+ "20", "01", "00", "1c", "01", "10", "00", "18", "20", "3c",
+ "78", "00", "00", "10", "00", "04", "00", "00", "00", "05",
+ "00", "1a", "00", "04", "00", "00", "00", "0a"};
+
+uint16_t pcep_update_cisco_pce_hexbyte_strs_length = 100;
+const char *pcep_update_cisco_pce_hexbyte_strs[] = {
+ "20", "0b", "00", "64", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "18", "80", "00", "f0", "89", "00", "07", "00", "0c",
+ "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01",
+ "07", "10", "00", "28", "24", "0c", "10", "01", "04", "65", "50", "00",
+ "0a", "0a", "0a", "05", "24", "0c", "10", "01", "04", "65", "20", "00",
+ "0a", "0a", "0a", "02", "24", "0c", "10", "01", "04", "65", "10", "00",
+ "0a", "0a", "0a", "01", "06", "10", "00", "0c", "00", "00", "00", "02",
+ "41", "f0", "00", "00"};
+
+uint16_t pcep_report_cisco_pcc_hexbyte_strs_length = 148;
+const char *pcep_report_cisco_pcc_hexbyte_strs[] = {
+ "20", "0a", "00", "94", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "00", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "3c", "80", "00", "f0", "09", "00", "12", "00", "10",
+ "0a", "0a", "0a", "06", "00", "02", "00", "0f", "0a", "0a", "0a", "06",
+ "0a", "0a", "0a", "01", "00", "11", "00", "0d", "63", "66", "67", "5f",
+ "52", "36", "2d", "74", "6f", "2d", "52", "31", "00", "00", "00", "00",
+ "ff", "e1", "00", "06", "00", "00", "05", "dd", "70", "00", "00", "00",
+ "07", "10", "00", "04", "09", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "00", "00", "00", "00", "00", "07", "07", "01", "00",
+ "05", "12", "00", "08", "00", "00", "00", "00", "05", "52", "00", "08",
+ "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "00", "02",
+ "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "01", "04",
+ "41", "80", "00", "00"};
+
+/* Cisco PcInitiate with the following objects:
+ * SRP, LSP, Endpoint, Inter-layer, Switch-layer, ERO
+ */
+uint16_t pcep_initiate_cisco_pcc_hexbyte_strs_length = 104;
+const char *pcep_initiate_cisco_pcc_hexbyte_strs[] = {
+ "20", "0c", "00", "68", "21", "10", "00", "14", "00", "00", "00", "00",
+ "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01",
+ "20", "10", "00", "30", "00", "00", "00", "89", "00", "11", "00", "13",
+ "50", "4f", "4c", "31", "5f", "50", "43", "49", "4e", "49", "54", "41",
+ "54", "45", "5f", "54", "45", "53", "54", "00", "00", "07", "00", "0c",
+ "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01",
+ "04", "10", "00", "0c", "0a", "0a", "0a", "0a", "0a", "0a", "0a", "04",
+ "24", "10", "00", "08", "00", "00", "01", "4d", "25", "10", "00", "08",
+ "00", "00", "00", "64", "07", "10", "00", "04"};
+
+struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class,
+ uint8_t obj2_class, uint8_t obj3_class,
+ uint8_t obj4_class);
+int convert_hexstrs_to_binary(const char *hexbyte_strs[],
+ uint16_t hexbyte_strs_length);
+
+int pcep_tools_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_tools_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+void pcep_tools_test_setup(void)
+{
+}
+
+void pcep_tools_test_teardown(void)
+{
+}
+
+/* Reads an array of hexbyte strs, and writes them to a temporary file.
+ * The caller should close the returned file. */
+int convert_hexstrs_to_binary(const char *hexbyte_strs[],
+ uint16_t hexbyte_strs_length)
+{
+ int fd = fileno(tmpfile());
+
+ int i = 0;
+ for (; i < hexbyte_strs_length; i++) {
+ uint8_t byte = (uint8_t)strtol(hexbyte_strs[i], 0, 16);
+ if (write(fd, (char *)&byte, 1) < 0) {
+ return -1;
+ }
+ }
+
+ /* Go back to the beginning of the file */
+ lseek(fd, 0, SEEK_SET);
+ return fd;
+}
+
+static bool pcep_obj_has_tlv(struct pcep_object_header *obj_hdr)
+{
+ if (obj_hdr->tlv_list == NULL) {
+ return false;
+ }
+
+ return (obj_hdr->tlv_list->num_entries > 0);
+}
+
+void test_pcep_msg_read_pcep_initiate()
+{
+ int fd = convert_hexstrs_to_binary(pcep_initiate_hexbyte_strs,
+ pcep_initiate_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ /* SRP object */
+ double_linked_list_node *node = msg->obj_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* LSP object and its TLV*/
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_EQUAL(((struct pcep_object_lsp *)obj_hdr)->plsp_id, 0);
+ CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_d);
+ CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_a);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_s);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_r);
+ CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_c);
+
+ /* LSP TLV */
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8);
+
+ /* Endpoints object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 2);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ struct pcep_object_ro_subobj *subobj_hdr =
+ (struct pcep_object_ro_subobj *)subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4);
+ struct in_addr ero_subobj_ip;
+ inet_pton(AF_INET, "10.0.1.1", &ero_subobj_ip);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr,
+ ero_subobj_ip.s_addr);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24);
+
+ subobj_hdr =
+ (struct pcep_object_ro_subobj *)subobj_node->next_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4);
+ inet_pton(AF_INET, "10.0.7.4", &ero_subobj_ip);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr,
+ ero_subobj_ip.s_addr);
+ CU_ASSERT_EQUAL(
+ ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+
+void test_pcep_msg_read_pcep_initiate2()
+{
+ int fd = convert_hexstrs_to_binary(pcep_initiate2_hexbyte_strs,
+ pcep_initiate2_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate2_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ /* SRP object */
+ double_linked_list_node *node = msg->obj_list->head;
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ /* TODO test the TLVs */
+
+ /* LSP object and its TLV*/
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+
+ /* LSP TLV */
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8);
+
+ /* Endpoints object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ CU_ASSERT_PTR_NULL(subobj_node);
+ /* We no longer support draft07 SR sub-object type=5, and only support
+ type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct
+ pcep_object_ro_subobj *) subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR);
+ struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *)
+ subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m);
+ CU_ASSERT_FALSE(subobj_sr->flag_c);
+ CU_ASSERT_FALSE(subobj_sr->flag_s);
+ CU_ASSERT_FALSE(subobj_sr->flag_f);
+ CU_ASSERT_EQUAL(subobj_sr->sid, 65576960);
+ CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data),
+ 0x01010101);
+ */
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open()
+{
+ int fd = convert_hexstrs_to_binary(pcep_open_odl_hexbyte_strs,
+ pcep_open_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+
+ /* Verify the Open message */
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_OPEN);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 24);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+
+ /* Open TLV: Stateful PCE Capability */
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 2);
+ double_linked_list_node *tlv_node = obj_hdr->tlv_list->head;
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)tlv_node->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+
+ /* Open TLV: SR PCE Capability */
+ tlv_node = tlv_node->next_node;
+ tlv = (struct pcep_object_tlv_header *)tlv_node->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_update()
+{
+ int fd = convert_hexstrs_to_binary(pcep_update_hexbyte_strs,
+ pcep_update_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 3);
+
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_update_hexbyte_strs_length);
+
+ /* Verify each of the object types */
+
+ double_linked_list_node *node = msg->obj_list->head;
+
+ /* SRP object */
+ struct pcep_object_header *obj_hdr =
+ (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20);
+ CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr));
+
+ /* SRP TLV */
+ CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1);
+ struct pcep_object_tlv_header *tlv =
+ (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data;
+ CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4);
+ /* TODO verify the path setup type */
+
+ /* LSP object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length,
+ pcep_object_get_length_by_hdr(obj_hdr));
+ CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr));
+
+ /* ERO object */
+ node = node->next_node;
+ obj_hdr = (struct pcep_object_header *)node->data;
+ CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16);
+
+ /* ERO Subobjects */
+ double_linked_list *ero_subobj_list =
+ ((struct pcep_object_ro *)obj_hdr)->sub_objects;
+ CU_ASSERT_PTR_NOT_NULL(ero_subobj_list);
+ CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0);
+ double_linked_list_node *subobj_node = ero_subobj_list->head;
+ CU_ASSERT_PTR_NULL(subobj_node);
+ /* We no longer support draft07 SR sub-object type=5, and only support
+ type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct
+ pcep_object_ro_subobj *) subobj_node->data;
+ CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR);
+ struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *)
+ subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m);
+ CU_ASSERT_FALSE(subobj_sr->flag_c);
+ CU_ASSERT_FALSE(subobj_sr->flag_s);
+ CU_ASSERT_FALSE(subobj_sr->flag_f);
+ CU_ASSERT_EQUAL(subobj_sr->sid, 65576960);
+ CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data),
+ 0x01010101);
+ */
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open_initiate()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_open_initiate_odl_hexbyte_strs,
+ pcep_open_initiate_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 2);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+
+ msg = (struct pcep_message *)msg_list->head->next_node->data;
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate2_hexbyte_strs_length);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_open_cisco_pce()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_open_cisco_pce_hexbyte_strs,
+ pcep_open_cisco_pce_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_open_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1);
+
+ /* Open object */
+ struct pcep_object_open *open =
+ (struct pcep_object_open *)msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(open->header.object_class, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_EQUAL(open->header.object_type, PCEP_OBJ_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open->header.encoded_object_length, 24);
+ CU_ASSERT_EQUAL(open->open_deadtimer, 120);
+ CU_ASSERT_EQUAL(open->open_keepalive, 60);
+ CU_ASSERT_EQUAL(open->open_sid, 0);
+ CU_ASSERT_EQUAL(open->open_version, 1);
+ CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list);
+ CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 2);
+
+ /* Stateful PCE Capability TLV */
+ double_linked_list_node *tlv_node = open->header.tlv_list->head;
+ struct pcep_object_tlv_stateful_pce_capability *pce_cap_tlv =
+ (struct pcep_object_tlv_stateful_pce_capability *)
+ tlv_node->data;
+ CU_ASSERT_EQUAL(pce_cap_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(pce_cap_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_TRUE(pce_cap_tlv->flag_u_lsp_update_capability);
+ CU_ASSERT_TRUE(pce_cap_tlv->flag_i_lsp_instantiation_capability);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_s_include_db_version);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_t_triggered_resync);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_d_delta_lsp_sync);
+ CU_ASSERT_FALSE(pce_cap_tlv->flag_f_triggered_initial_sync);
+
+ /* SR PCE Capability TLV */
+ tlv_node = tlv_node->next_node;
+ struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
+ (struct pcep_object_tlv_sr_pce_capability *)tlv_node->data;
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_n);
+ CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_x);
+ CU_ASSERT_EQUAL(sr_pce_cap_tlv->max_sid_depth, 10);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_update_cisco_pce()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_update_cisco_pce_hexbyte_strs,
+ pcep_update_cisco_pce_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_update_cisco_pce_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 1);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* SRP Path Setup Type TLV */
+ double_linked_list_node *tlv_node = srp->header.tlv_list->head;
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ (struct pcep_object_tlv_path_setup_type *)tlv_node->data;
+ CU_ASSERT_EQUAL(pst_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 24);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_c);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* LSP Vendor Info TLV */
+ tlv_node = lsp->header.tlv_list->head;
+ struct pcep_object_tlv_vendor_info *vendor_tlv =
+ (struct pcep_object_tlv_vendor_info *)tlv_node->data;
+ CU_ASSERT_EQUAL(vendor_tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO);
+ CU_ASSERT_EQUAL(vendor_tlv->header.encoded_tlv_length, 12);
+ CU_ASSERT_EQUAL(vendor_tlv->enterprise_number, 9);
+ CU_ASSERT_EQUAL(vendor_tlv->enterprise_specific_info, 0x00030004);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 40);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(ero->sub_objects);
+ CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 3);
+
+ /* ERO Subobjects */
+ double_linked_list_node *ero_subobj_node = ero->sub_objects->head;
+ struct pcep_ro_subobj_sr *sr_subobj_ipv4_node =
+ (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73748480);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a05));
+
+ ero_subobj_node = ero_subobj_node->next_node;
+ sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73736192);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a02));
+
+ ero_subobj_node = ero_subobj_node->next_node;
+ sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data;
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type,
+ RO_SUBOBJ_TYPE_SR);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type,
+ PCEP_SR_SUBOBJ_NAI_IPV4_NODE);
+ CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f);
+ CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s);
+ CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73732096);
+ CU_ASSERT_EQUAL(
+ *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data),
+ htonl(0x0a0a0a01));
+
+ /* Metric object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_metric *metric =
+ (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_FALSE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE);
+ CU_ASSERT_EQUAL(metric->value, 30.0);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_report_cisco_pcc()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_report_cisco_pcc_hexbyte_strs,
+ pcep_report_cisco_pcc_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_REPORT);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_report_cisco_pcc_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 8);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 0);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* SRP Path Setup Type TLV */
+ double_linked_list_node *tlv_node = srp->header.tlv_list->head;
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ (struct pcep_object_tlv_path_setup_type *)tlv_node->data;
+ CU_ASSERT_EQUAL(pst_tlv->header.type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE);
+ CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4);
+ CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ /* The TLV with ID 65505 is not recognized, and its not in the list */
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_FALSE(lsp->flag_c);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* LSP IPv4 LSP Identifier TLV */
+ tlv_node = lsp->header.tlv_list->head;
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id =
+ (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv_node->data;
+ CU_ASSERT_EQUAL(ipv4_lsp_id->header.type,
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->header.encoded_tlv_length, 16);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_sender.s_addr,
+ htonl(0x0a0a0a06));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_endpoint.s_addr,
+ htonl(0x0a0a0a01));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->extended_tunnel_id.s_addr,
+ htonl(0x0a0a0a06));
+ CU_ASSERT_EQUAL(ipv4_lsp_id->tunnel_id, 15);
+ CU_ASSERT_EQUAL(ipv4_lsp_id->lsp_id, 2);
+
+ /* LSP Symbolic Path Name TLV */
+ tlv_node = tlv_node->next_node;
+ struct pcep_object_tlv_symbolic_path_name *sym_path_name =
+ (struct pcep_object_tlv_symbolic_path_name *)tlv_node->data;
+ CU_ASSERT_EQUAL(sym_path_name->header.type,
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME);
+ CU_ASSERT_EQUAL(sym_path_name->header.encoded_tlv_length, 13);
+ CU_ASSERT_EQUAL(sym_path_name->symbolic_path_name_length, 13);
+ CU_ASSERT_EQUAL(
+ strncmp(sym_path_name->symbolic_path_name, "cfg_R6-to-R1", 13),
+ 0);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(ero->sub_objects);
+ CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 0);
+
+ /* LSPA object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lspa *lspa =
+ (struct pcep_object_lspa *)obj_node->data;
+ CU_ASSERT_EQUAL(lspa->header.object_class, PCEP_OBJ_CLASS_LSPA);
+ CU_ASSERT_EQUAL(lspa->header.object_type, PCEP_OBJ_TYPE_LSPA);
+ CU_ASSERT_EQUAL(lspa->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NULL(lspa->header.tlv_list);
+ CU_ASSERT_TRUE(lspa->flag_local_protection);
+ CU_ASSERT_EQUAL(lspa->holding_priority, 7);
+ CU_ASSERT_EQUAL(lspa->setup_priority, 7);
+ CU_ASSERT_EQUAL(lspa->lspa_include_all, 0);
+ CU_ASSERT_EQUAL(lspa->lspa_include_any, 0);
+ CU_ASSERT_EQUAL(lspa->lspa_exclude_any, 0);
+
+ /* Bandwidth object 1 */
+ obj_node = obj_node->next_node;
+ struct pcep_object_bandwidth *bandwidth =
+ (struct pcep_object_bandwidth *)obj_node->data;
+ CU_ASSERT_EQUAL(bandwidth->header.object_class,
+ PCEP_OBJ_CLASS_BANDWIDTH);
+ CU_ASSERT_EQUAL(bandwidth->header.object_type,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ);
+ CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8);
+ CU_ASSERT_EQUAL(bandwidth->bandwidth, 0);
+
+ /* Bandwidth object 2 */
+ obj_node = obj_node->next_node;
+ bandwidth = (struct pcep_object_bandwidth *)obj_node->data;
+ CU_ASSERT_EQUAL(bandwidth->header.object_class,
+ PCEP_OBJ_CLASS_BANDWIDTH);
+ CU_ASSERT_EQUAL(bandwidth->header.object_type,
+ PCEP_OBJ_TYPE_BANDWIDTH_CISCO);
+ CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8);
+ CU_ASSERT_EQUAL(bandwidth->bandwidth, 0);
+
+ /* Metric object 1 */
+ obj_node = obj_node->next_node;
+ struct pcep_object_metric *metric =
+ (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_FALSE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE);
+ CU_ASSERT_EQUAL(metric->value, 0);
+
+ /* Metric object 2 */
+ obj_node = obj_node->next_node;
+ metric = (struct pcep_object_metric *)obj_node->data;
+ CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC);
+ CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC);
+ CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(metric->header.tlv_list);
+ CU_ASSERT_TRUE(metric->flag_b);
+ CU_ASSERT_FALSE(metric->flag_c);
+ CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_AGGREGATE_BW);
+ CU_ASSERT_EQUAL(metric->value, 16.0);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_pcep_msg_read_pcep_initiate_cisco_pcc()
+{
+ int fd = convert_hexstrs_to_binary(
+ pcep_initiate_cisco_pcc_hexbyte_strs,
+ pcep_initiate_cisco_pcc_hexbyte_strs_length);
+ double_linked_list *msg_list = pcep_msg_read(fd);
+ CU_ASSERT_PTR_NOT_NULL(msg_list);
+ CU_ASSERT_EQUAL(msg_list->num_entries, 1);
+
+ struct pcep_message *msg = (struct pcep_message *)msg_list->head->data;
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE);
+ CU_ASSERT_EQUAL(msg->encoded_message_length,
+ pcep_initiate_cisco_pcc_hexbyte_strs_length);
+ CU_ASSERT_EQUAL(msg->obj_list->num_entries, 6);
+
+ /* SRP object */
+ double_linked_list_node *obj_node = msg->obj_list->head;
+ struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data;
+ CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP);
+ CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP);
+ CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20);
+ CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list);
+ CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(srp->srp_id_number, 1);
+ CU_ASSERT_FALSE(srp->flag_lsp_remove);
+
+ /* LSP object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data;
+ CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP);
+ CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
+ CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 48);
+ CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(lsp->plsp_id, 0);
+ CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
+ CU_ASSERT_TRUE(lsp->flag_a);
+ CU_ASSERT_TRUE(lsp->flag_d);
+ CU_ASSERT_TRUE(lsp->flag_c);
+ CU_ASSERT_FALSE(lsp->flag_r);
+ CU_ASSERT_FALSE(lsp->flag_s);
+
+ /* Endpoint object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_endpoints_ipv4 *endpoint =
+ (struct pcep_object_endpoints_ipv4 *)obj_node->data;
+ CU_ASSERT_EQUAL(endpoint->header.object_class,
+ PCEP_OBJ_CLASS_ENDPOINTS);
+ CU_ASSERT_EQUAL(endpoint->header.object_type,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4);
+ CU_ASSERT_EQUAL(endpoint->header.encoded_object_length, 12);
+ CU_ASSERT_PTR_NULL(endpoint->header.tlv_list);
+ CU_ASSERT_EQUAL(endpoint->src_ipv4.s_addr, htonl(0x0a0a0a0a));
+ CU_ASSERT_EQUAL(endpoint->dst_ipv4.s_addr, htonl(0x0a0a0a04));
+
+ /* Inter-Layer object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_inter_layer *inter_layer =
+ (struct pcep_object_inter_layer *)obj_node->data;
+ CU_ASSERT_EQUAL(inter_layer->header.object_class,
+ PCEP_OBJ_CLASS_INTER_LAYER);
+ CU_ASSERT_EQUAL(inter_layer->header.object_type,
+ PCEP_OBJ_TYPE_INTER_LAYER);
+ CU_ASSERT_EQUAL(inter_layer->header.encoded_object_length, 8);
+ CU_ASSERT_PTR_NULL(inter_layer->header.tlv_list);
+ CU_ASSERT_TRUE(inter_layer->flag_i);
+ CU_ASSERT_FALSE(inter_layer->flag_m);
+ CU_ASSERT_TRUE(inter_layer->flag_t);
+
+ /* Switch-Layer object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_switch_layer *switch_layer =
+ (struct pcep_object_switch_layer *)obj_node->data;
+ CU_ASSERT_EQUAL(switch_layer->header.object_class,
+ PCEP_OBJ_CLASS_SWITCH_LAYER);
+ CU_ASSERT_EQUAL(switch_layer->header.object_type,
+ PCEP_OBJ_TYPE_SWITCH_LAYER);
+ CU_ASSERT_EQUAL(switch_layer->header.encoded_object_length, 8);
+ CU_ASSERT_PTR_NULL(switch_layer->header.tlv_list);
+ CU_ASSERT_PTR_NOT_NULL(switch_layer->switch_layer_rows);
+ CU_ASSERT_EQUAL(switch_layer->switch_layer_rows->num_entries, 1);
+ struct pcep_object_switch_layer_row *switch_layer_row =
+ (struct pcep_object_switch_layer_row *)
+ switch_layer->switch_layer_rows->head->data;
+ CU_ASSERT_EQUAL(switch_layer_row->lsp_encoding_type, 0);
+ CU_ASSERT_EQUAL(switch_layer_row->switching_type, 0);
+ CU_ASSERT_FALSE(switch_layer_row->flag_i);
+
+ /* ERO object */
+ obj_node = obj_node->next_node;
+ struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data;
+ CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO);
+ CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO);
+ CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4);
+ CU_ASSERT_PTR_NULL(ero->header.tlv_list);
+
+ pcep_msg_free_message_list(msg_list);
+ close(fd);
+}
+
+void test_validate_message_header()
+{
+ uint8_t pcep_message_invalid_version[] = {0x40, 0x01, 0x04, 0x00};
+ uint8_t pcep_message_invalid_flags[] = {0x22, 0x01, 0x04, 0x00};
+ uint8_t pcep_message_invalid_length[] = {0x20, 0x01, 0x00, 0x00};
+ uint8_t pcep_message_invalid_type[] = {0x20, 0xff, 0x04, 0x00};
+ uint8_t pcep_message_valid[] = {0x20, 0x01, 0x00, 0x04};
+
+ /* Verify invalid message header version */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_version)
+ < 0);
+
+ /* Verify invalid message header flags */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_flags)
+ < 0);
+
+ /* Verify invalid message header lengths */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_length)
+ < 0);
+ pcep_message_invalid_length[3] = 0x05;
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_length)
+ < 0);
+
+ /* Verify invalid message header types */
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0);
+ pcep_message_invalid_type[1] = 0x00;
+ CU_ASSERT_TRUE(
+ pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0);
+
+ /* Verify a valid message header */
+ CU_ASSERT_EQUAL(pcep_decode_validate_msg_header(pcep_message_valid), 4);
+}
+
+/* Internal util function */
+struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class,
+ uint8_t obj2_class, uint8_t obj3_class,
+ uint8_t obj4_class)
+{
+ struct pcep_message *msg =
+ pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ msg->obj_list = dll_initialize();
+ msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES,
+ sizeof(struct pcep_message_header));
+ msg->msg_header->type = msg_type;
+ msg->encoded_message = NULL;
+
+ if (obj1_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj1_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj2_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj2_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj3_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj3_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ if (obj4_class > 0) {
+ struct pcep_object_header *obj_hdr = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_object_header));
+ obj_hdr->object_class = obj4_class;
+ obj_hdr->tlv_list = NULL;
+ dll_append(msg->obj_list, obj_hdr);
+ }
+
+ return msg;
+}
+
+void test_validate_message_objects()
+{
+ /* Valid Open message */
+ struct pcep_message *msg =
+ create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid KeepAlive message */
+ msg = create_message(PCEP_TYPE_KEEPALIVE, 0, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid PcReq message */
+ /* Using object_class=255 to verify it can take any object */
+ msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP,
+ PCEP_OBJ_CLASS_ENDPOINTS, any_obj_class, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid PcRep message */
+ msg = create_message(PCEP_TYPE_PCREP, PCEP_OBJ_CLASS_RP, any_obj_class,
+ 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Notify message */
+ msg = create_message(PCEP_TYPE_PCNOTF, PCEP_OBJ_CLASS_NOTF,
+ any_obj_class, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Error message */
+ msg = create_message(PCEP_TYPE_ERROR, PCEP_OBJ_CLASS_ERROR,
+ any_obj_class, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Close message */
+ msg = create_message(PCEP_TYPE_CLOSE, PCEP_OBJ_CLASS_CLOSE, 0, 0, 0);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Report message */
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Update message */
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Valid Initiate message */
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP,
+ PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class);
+ CU_ASSERT_TRUE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+}
+
+void test_validate_message_objects_invalid()
+{
+ /* unsupported message ID = 0
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ struct pcep_message *msg = create_message(0, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Open message
+ * {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_OPEN, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_OPEN, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, any_obj_class,
+ 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* KeepAlive message
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_KEEPALIVE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* PcReq message
+ * {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT, ANY_OBJECT}
+ */
+ msg = create_message(PCEP_TYPE_PCREQ, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP, any_obj_class,
+ 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* PcRep message
+ * {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_PCREP, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCREP, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Notify message
+ * {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_PCNOTF, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_PCNOTF, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Error message
+ * {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_ERROR, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_ERROR, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Close message
+ * {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(PCEP_TYPE_CLOSE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_CLOSE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* unsupported message ID = 8
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(8, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* unsupported message ID = 9
+ * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */
+ msg = create_message(9, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Report message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_REPORT, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Update message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_UPDATE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ /* Initiate message
+ * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */
+ msg = create_message(PCEP_TYPE_INITIATE, 0, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, any_obj_class, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+
+ msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP,
+ any_obj_class, 0, 0);
+ CU_ASSERT_FALSE(validate_message_objects(msg));
+ pcep_msg_free_message(msg);
+}
diff --git a/pceplib/test/pcep_msg_tools_test.h b/pceplib/test/pcep_msg_tools_test.h
new file mode 100644
index 0000000000..dc66390801
--- /dev/null
+++ b/pceplib/test/pcep_msg_tools_test.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MSG_TOOLS_TEST_H_
+#define PCEP_MSG_TOOLS_TEST_H_
+
+
+int pcep_tools_test_suite_setup(void);
+int pcep_tools_test_suite_teardown(void);
+void pcep_tools_test_setup(void);
+void pcep_tools_test_teardown(void);
+void test_pcep_msg_read_pcep_initiate(void);
+void test_pcep_msg_read_pcep_initiate2(void);
+void test_pcep_msg_read_pcep_update(void);
+void test_pcep_msg_read_pcep_open(void);
+void test_pcep_msg_read_pcep_open_initiate(void);
+void test_validate_message_header(void);
+void test_validate_message_objects(void);
+void test_validate_message_objects_invalid(void);
+void test_pcep_msg_read_pcep_open_cisco_pce(void);
+void test_pcep_msg_read_pcep_update_cisco_pce(void);
+void test_pcep_msg_read_pcep_report_cisco_pcc(void);
+void test_pcep_msg_read_pcep_initiate_cisco_pcc(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_pcc_api_test.c b/pceplib/test/pcep_pcc_api_test.c
new file mode 100644
index 0000000000..c227dc1a3d
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_test.c
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <netdb.h> // gethostbyname
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_pcc_api.h"
+#include "pcep_pcc_api_test.h"
+#include "pcep_socket_comm_mock.h"
+#include "pcep_utils_memory.h"
+
+extern pcep_event_queue *session_logic_event_queue_;
+extern const char MESSAGE_RECEIVED_STR[];
+extern const char UNKNOWN_EVENT_STR[];
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_pcc_api_test_suite_setup()
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_pcc_api_test_suite_teardown()
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_pcc_api_test_setup()
+{
+ setup_mock_socket_comm_info();
+}
+
+
+void pcep_pcc_api_test_teardown()
+{
+ teardown_mock_socket_comm_info();
+}
+
+/*
+ * Unit test cases
+ */
+
+void test_initialize_pcc()
+{
+ CU_ASSERT_TRUE(initialize_pcc());
+ /* Give the PCC time to initialize */
+ sleep(1);
+ CU_ASSERT_TRUE(destroy_pcc());
+}
+
+void test_connect_pce()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_connect_pce_ipv6()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct in6_addr dest_address;
+ dest_address.__in6_u.__u6_addr32[0] = 0;
+ dest_address.__in6_u.__u6_addr32[1] = 0;
+ dest_address.__in6_u.__u6_addr32[2] = 0;
+ dest_address.__in6_u.__u6_addr32[3] = htonl(1);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce_ipv6(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_connect_pce_with_src_ip()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config->src_ip.src_ipv4.s_addr = 0x0a0a0102;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+
+ pcep_msg_free_message(open_msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+void test_disconnect_pce()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ initialize_pcc();
+
+ pcep_session *session = connect_pce(config, &dest_address);
+ disconnect_pce(session);
+
+ CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 2);
+
+ /* First there should be an open message from connect_pce() */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Then there should be a close message from disconnect_pce() */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_CLOSE);
+
+ pcep_msg_free_message(msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ destroy_pcc();
+}
+
+
+void test_send_message()
+{
+ pcep_configuration *config = create_default_pcep_configuration();
+ struct hostent *host_info = gethostbyname("localhost");
+ struct in_addr dest_address;
+
+ initialize_pcc();
+
+ memcpy(&dest_address, host_info->h_addr, host_info->h_length);
+ pcep_session *session = connect_pce(config, &dest_address);
+ verify_socket_comm_times_called(0, 0, 1, 1, 0, 0, 0);
+
+ struct pcep_message *msg = pcep_msg_create_keepalive();
+ send_message(session, msg, false);
+
+ verify_socket_comm_times_called(0, 0, 1, 2, 0, 0, 0);
+
+ pcep_msg_free_message(msg);
+ destroy_pcep_session(session);
+ destroy_pcep_configuration(config);
+
+ destroy_pcc();
+}
+
+void test_event_queue()
+{
+ /* This initializes the event_queue */
+ CU_ASSERT_TRUE(initialize_pcc());
+
+ /* Verify correct behavior when the queue is empty */
+ CU_ASSERT_TRUE(event_queue_is_empty());
+ CU_ASSERT_EQUAL(event_queue_num_events_available(), 0);
+ CU_ASSERT_PTR_NULL(event_queue_get_event());
+ destroy_pcep_event(NULL);
+
+ /* Create an empty event and put it on the queue */
+ pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event));
+ memset(event, 0, sizeof(pcep_event));
+ pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex);
+ queue_enqueue(session_logic_event_queue_->event_queue, event);
+ pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex);
+
+ /* Verify correct behavior when there is an entry in the queue */
+ CU_ASSERT_FALSE(event_queue_is_empty());
+ CU_ASSERT_EQUAL(event_queue_num_events_available(), 1);
+ pcep_event *queued_event = event_queue_get_event();
+ CU_ASSERT_PTR_NOT_NULL(queued_event);
+ CU_ASSERT_PTR_EQUAL(event, queued_event);
+ destroy_pcep_event(queued_event);
+
+ CU_ASSERT_TRUE(destroy_pcc());
+}
+
+void test_get_event_type_str()
+{
+ CU_ASSERT_EQUAL(strcmp(get_event_type_str(MESSAGE_RECEIVED),
+ MESSAGE_RECEIVED_STR),
+ 0);
+ CU_ASSERT_EQUAL(strcmp(get_event_type_str(1000), UNKNOWN_EVENT_STR), 0);
+}
diff --git a/pceplib/test/pcep_pcc_api_test.h b/pceplib/test/pcep_pcc_api_test.h
new file mode 100644
index 0000000000..d3db96e0ce
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_PCC_API_TEST_
+#define PCEP_PCC_API_TEST_
+
+int pcep_pcc_api_test_suite_setup(void);
+int pcep_pcc_api_test_suite_teardown(void);
+void pcep_pcc_api_test_setup(void);
+void pcep_pcc_api_test_teardown(void);
+void test_initialize_pcc(void);
+void test_connect_pce(void);
+void test_connect_pce_ipv6(void);
+void test_connect_pce_with_src_ip(void);
+void test_disconnect_pce(void);
+void test_send_message(void);
+void test_event_queue(void);
+void test_get_event_type_str(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_pcc_api_tests.c b/pceplib/test/pcep_pcc_api_tests.c
new file mode 100644
index 0000000000..04895d6340
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_tests.c
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_pcc_api_test.h"
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_pcc_api_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP PCC API Test Suite",
+ pcep_pcc_api_test_suite_setup, // suite setup and cleanup
+ // function pointers
+ pcep_pcc_api_test_suite_teardown,
+ pcep_pcc_api_test_setup, // test case setup function pointer
+ pcep_pcc_api_test_teardown); // test case teardown function
+ // pointer
+
+ CU_add_test(test_pcc_api_suite, "test_initialize_pcc",
+ test_initialize_pcc);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce", test_connect_pce);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce_ipv6",
+ test_connect_pce_ipv6);
+ CU_add_test(test_pcc_api_suite, "test_connect_pce_with_src_ip",
+ test_connect_pce_with_src_ip);
+ CU_add_test(test_pcc_api_suite, "test_disconnect_pce",
+ test_disconnect_pce);
+ CU_add_test(test_pcc_api_suite, "test_send_message", test_send_message);
+ CU_add_test(test_pcc_api_suite, "test_event_queue", test_event_queue);
+ CU_add_test(test_pcc_api_suite, "test_get_event_type_str",
+ test_get_event_type_str);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_pcc_api_tests_valgrind.sh b/pceplib/test/pcep_pcc_api_tests_valgrind.sh
new file mode 100755
index 0000000000..74494b7521
--- /dev/null
+++ b/pceplib/test/pcep_pcc_api_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_pcc_api_tests
diff --git a/pceplib/test/pcep_session_logic_loop_test.c b/pceplib/test/pcep_session_logic_loop_test.c
new file mode 100644
index 0000000000..38fabd4ccd
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_loop_test.c
@@ -0,0 +1,219 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_msg_encoding.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_session_logic_loop_test.h"
+
+
+extern pcep_session_logic_handle *session_logic_handle_;
+extern pcep_event_queue *session_logic_event_queue_;
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_loop_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_loop_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_loop_test_setup()
+{
+ /* We need to setup the session_logic_handle_ without starting the
+ * thread */
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+ session_logic_handle_->active = true;
+ session_logic_handle_->session_logic_condition = false;
+ session_logic_handle_->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ session_logic_handle_->session_event_queue = queue_initialize();
+ pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
+ NULL);
+ pthread_mutex_init(&(session_logic_handle_->session_logic_mutex), NULL);
+ pthread_mutex_init(&(session_logic_handle_->session_list_mutex), NULL);
+
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+}
+
+
+void pcep_session_logic_loop_test_teardown()
+{
+ ordered_list_destroy(session_logic_handle_->session_list);
+ queue_destroy(session_logic_handle_->session_event_queue);
+ pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex));
+ pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex));
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ session_logic_handle_ = NULL;
+
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+ session_logic_event_queue_ = NULL;
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_session_logic_loop_null_data()
+{
+ /* Just testing that it does not core dump */
+ session_logic_loop(NULL);
+}
+
+
+void test_session_logic_loop_inactive()
+{
+ session_logic_handle_->active = false;
+
+ session_logic_loop(session_logic_handle_);
+}
+
+
+void test_session_logic_msg_ready_handler()
+{
+ /* Just testing that it does not core dump */
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(NULL, 0), -1);
+
+ /* Read from an empty file should return 0, thus
+ * session_logic_msg_ready_handler returns -1 */
+ int fd = fileno(tmpfile());
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd), 0);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL(socket_event);
+ CU_ASSERT_TRUE(socket_event->socket_closed);
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+
+ /* A pcep_session_event should be created */
+ struct pcep_versioning *versioning = create_default_pcep_versioning();
+ struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive();
+ pcep_encode_message(keep_alive_msg, versioning);
+ int retval = write(fd, (char *)keep_alive_msg->encoded_message,
+ keep_alive_msg->encoded_message_length);
+ CU_ASSERT_TRUE(retval > 0);
+ lseek(fd, 0, SEEK_SET);
+ CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd),
+ keep_alive_msg->encoded_message_length);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL(socket_event);
+ CU_ASSERT_FALSE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET);
+ CU_ASSERT_PTR_NOT_NULL(socket_event->received_msg_list);
+ pcep_msg_free_message_list(socket_event->received_msg_list);
+ pcep_msg_free_message(keep_alive_msg);
+ destroy_pcep_versioning(versioning);
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+ close(fd);
+}
+
+
+void test_session_logic_conn_except_notifier()
+{
+ /* Just testing that it does not core dump */
+ session_logic_conn_except_notifier(NULL, 1);
+
+ /* A pcep_session_event should be created */
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ session_logic_conn_except_notifier(&session, 10);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event);
+ CU_ASSERT_TRUE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET);
+ CU_ASSERT_PTR_NULL(socket_event->received_msg_list);
+
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+}
+
+
+void test_session_logic_timer_expire_handler()
+{
+ /* Just testing that it does not core dump */
+ session_logic_timer_expire_handler(NULL, 42);
+
+ /* A pcep_session_event should be created */
+ pcep_session session;
+ memset(&session, 0, sizeof(pcep_session));
+ session.session_id = 100;
+ session_logic_timer_expire_handler(&session, 42);
+ CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries,
+ 1);
+ pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue(
+ session_logic_handle_->session_event_queue);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event);
+ CU_ASSERT_FALSE(socket_event->socket_closed);
+ CU_ASSERT_PTR_EQUAL(socket_event->session, &session);
+ CU_ASSERT_EQUAL(socket_event->expired_timer_id, 42);
+ CU_ASSERT_PTR_NULL(socket_event->received_msg_list);
+
+ pceplib_free(PCEPLIB_INFRA, socket_event);
+}
diff --git a/pceplib/test/pcep_session_logic_loop_test.h b/pceplib/test/pcep_session_logic_loop_test.h
new file mode 100644
index 0000000000..ae3c3e3753
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_loop_test.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_LOOP_TEST_H_
+#define PCEP_SESSION_LOGIC_LOOP_TEST_H_
+
+int pcep_session_logic_loop_test_suite_setup(void);
+int pcep_session_logic_loop_test_suite_teardown(void);
+void pcep_session_logic_loop_test_setup(void);
+void pcep_session_logic_loop_test_teardown(void);
+void test_session_logic_loop_null_data(void);
+void test_session_logic_loop_inactive(void);
+void test_session_logic_msg_ready_handler(void);
+void test_session_logic_conn_except_notifier(void);
+void test_session_logic_timer_expire_handler(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_states_test.c b/pceplib/test/pcep_session_logic_states_test.c
new file mode 100644
index 0000000000..f75c16e397
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_states_test.c
@@ -0,0 +1,919 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_mock.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_internals.h"
+#include "pcep_timers.h"
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_memory.h"
+#include "pcep_msg_objects.h"
+#include "pcep_msg_tools.h"
+#include "pcep_session_logic_states_test.h"
+
+/* Functions being tested */
+extern pcep_session_logic_handle *session_logic_handle_;
+extern pcep_event_queue *session_logic_event_queue_;
+
+static pcep_session_event event;
+static pcep_session session;
+/* A message list is a dll of struct pcep_messages_list_node items */
+static double_linked_list *msg_list;
+struct pcep_message *message;
+static bool free_msg_list;
+static bool msg_enqueued;
+/* Forward declaration */
+void destroy_message_for_test(void);
+void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown,
+ bool was_msg_enqueued);
+void test_handle_timer_event_open_keep_alive(void);
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_states_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_states_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_states_test_setup()
+{
+ session_logic_handle_ = pceplib_malloc(
+ PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
+ memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
+
+ session_logic_event_queue_ =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
+ memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue));
+ session_logic_event_queue_->event_queue = queue_initialize();
+
+ memset(&session, 0, sizeof(pcep_session));
+ session.pcc_config.keep_alive_seconds = 5;
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds = 5;
+ session.pcc_config.min_keep_alive_seconds = 1;
+ session.pcc_config.max_keep_alive_seconds = 10;
+ session.pcc_config.dead_timer_seconds = 5;
+ session.pcc_config.dead_timer_pce_negotiated_seconds = 5;
+ session.pcc_config.min_dead_timer_seconds = 1;
+ session.pcc_config.max_dead_timer_seconds = 10;
+ session.pcc_config.max_unknown_messages = 2;
+ memcpy(&session.pce_config, &session.pcc_config,
+ sizeof(pcep_configuration));
+ session.num_unknown_messages_time_queue = queue_initialize();
+
+ memset(&event, 0, sizeof(pcep_session_event));
+ event.socket_closed = false;
+ event.session = &session;
+
+ setup_mock_socket_comm_info();
+ free_msg_list = false;
+ msg_enqueued = false;
+}
+
+
+void pcep_session_logic_states_test_teardown()
+{
+ destroy_message_for_test();
+ pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
+ queue_destroy(session_logic_event_queue_->event_queue);
+ pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
+ session_logic_handle_ = NULL;
+ session_logic_event_queue_ = NULL;
+ queue_destroy_with_data(session.num_unknown_messages_time_queue);
+ teardown_mock_socket_comm_info();
+}
+
+void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown,
+ bool was_msg_enqueued)
+{
+ /* See the comments in destroy_message_for_test() about these 2
+ * variables */
+ free_msg_list = free_msg_list_at_teardown;
+ msg_enqueued = was_msg_enqueued;
+
+ message = pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message));
+ memset(message, 0, sizeof(struct pcep_message));
+
+ message->msg_header = pceplib_malloc(
+ PCEPLIB_MESSAGES, sizeof(struct pcep_message_header));
+ memset(message->msg_header, 0, sizeof(struct pcep_message_header));
+ message->obj_list = dll_initialize();
+ message->msg_header->type = msg_type;
+
+ msg_list = dll_initialize();
+ dll_append(msg_list, message);
+ event.received_msg_list = msg_list;
+}
+
+void destroy_message_for_test()
+{
+ /* Some test cases internally free the message list, so we dont
+ * want to double free it */
+ if (free_msg_list == true) {
+ /* This will destroy both the msg_list and the obj_list */
+ pcep_msg_free_message_list(msg_list);
+ }
+
+ /* Some tests cause the message to be enqueued and dont delete it,
+ * so we have to delete it here */
+ if (msg_enqueued == true) {
+ pcep_msg_free_message(message);
+ }
+}
+
+/*
+ * Test cases
+ */
+
+void test_handle_timer_event_dead_timer()
+{
+ /* Dead Timer expired */
+ event.expired_timer_id = session.timer_id_dead_timer = 100;
+
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_DEAD_TIMER_EXPIRED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* verify_socket_comm_times_called(
+ * initialized, teardown, connect, send_message, close_after_write,
+ * close, destroy); */
+ verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0);
+}
+
+
+void test_handle_timer_event_keep_alive()
+{
+ /* Keep Alive timer expired */
+ event.expired_timer_id = session.timer_id_keep_alive = 200;
+
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_keep_alive, TIMER_ID_NOT_SET);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+}
+
+
+void test_handle_timer_event_open_keep_wait()
+{
+ /* Open Keep Wait timer expired */
+ event.expired_timer_id = session.timer_id_open_keep_wait = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 1, 0, 0);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* If the state is not SESSION_STATE_PCEP_CONNECTED, then nothing should
+ * happen */
+ reset_mock_socket_comm_info();
+ session.session_state = SESSION_STATE_UNKNOWN;
+ event.expired_timer_id = session.timer_id_open_keep_wait = 300;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, 300);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_UNKNOWN);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+}
+
+
+void test_handle_timer_event_open_keep_alive()
+{
+ /* Open Keep Alive timer expired, but the Keep Alive should not be sent
+ * since the PCE Open has not been received yet */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = false;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+
+ /* Open Keep Alive timer expired, the Keep Alive should be sent,
+ * but the session should not be connected, since the PCC Open
+ * has not been accepted yet */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = true;
+ session.pce_open_rejected = false;
+ session.pcc_open_accepted = false;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+
+ /* Open Keep Alive timer expired, the Keep Alive should be sent,
+ * and the session is connected */
+ event.expired_timer_id = session.timer_id_open_keep_alive = 300;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.pce_open_keep_alive_sent = false;
+ session.pce_open_received = true;
+ session.pce_open_rejected = false;
+ session.pcc_open_accepted = true;
+ handle_timer_event(&event);
+
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+}
+
+
+void test_handle_socket_comm_event_null_params()
+{
+ /* Verify it doesnt core dump */
+ handle_socket_comm_event(NULL);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ reset_mock_socket_comm_info();
+
+ event.received_msg_list = NULL;
+ handle_socket_comm_event(&event);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+}
+
+
+void test_handle_socket_comm_event_close()
+{
+ event.socket_closed = true;
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_open()
+{
+ /*
+ * Test when a PCE Open is received, but the PCC Open has not been
+ * accepted yet
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, true);
+ struct pcep_object_open *open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ dll_append(message->obj_list, open_object);
+ session.pcc_open_accepted = false;
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.timer_id_open_keep_alive = 100;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_NOT_EQUAL(session.timer_id_open_keep_alive, 100);
+ /* A keep alive response should NOT be sent yet */
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /*
+ * Test when a PCE Open is received, and the PCC Open has been accepted
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, true);
+ reset_mock_socket_comm_info();
+ open_object = pcep_obj_create_open(1, 1, 1, NULL);
+ dll_append(message->obj_list, open_object);
+ session.pcc_open_accepted = true;
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_rejected);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ /* A keep alive response should be sent, accepting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /*
+ * Send a 2nd Open, an error should be sent
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_error *error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_RECVD_INVALID_OPEN_MSG,
+ error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_open_error()
+{
+ /* Test when the PCE rejects the PCC Open with an Error
+ * that a "corrected" Open message is sent. */
+
+ create_message_for_test(PCEP_TYPE_ERROR, false, true);
+ struct pcep_object_error *error_object = pcep_obj_create_error(
+ PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG);
+ struct pcep_object_open *error_open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ /* The configured [Keep-alive, Dead-timer] values are [5, 5],
+ * this error open object will request they be changed to [10, 10] */
+ error_open_object->open_keepalive = 10;
+ error_open_object->open_deadtimer = 10;
+ dll_append(message->obj_list, error_object);
+ dll_append(message->obj_list, error_open_object);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* Another Open should be sent */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* Check the Corrected Open Message */
+
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg_corrected =
+ pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg_corrected);
+ struct pcep_object_open *open_object_corrected =
+ (struct pcep_object_open *)pcep_obj_get(
+ open_msg_corrected->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_object_corrected);
+ /* Verify the Keep-alive and Dead timers have been negotiated */
+ CU_ASSERT_EQUAL(error_open_object->open_keepalive,
+ open_object_corrected->open_keepalive);
+ CU_ASSERT_EQUAL(error_open_object->open_deadtimer,
+ open_object_corrected->open_deadtimer);
+ CU_ASSERT_EQUAL(session.pcc_config.dead_timer_pce_negotiated_seconds,
+ open_object_corrected->open_deadtimer);
+ CU_ASSERT_EQUAL(
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds,
+ open_object_corrected->open_keepalive);
+ CU_ASSERT_NOT_EQUAL(
+ session.pcc_config.dead_timer_pce_negotiated_seconds,
+ session.pcc_config.dead_timer_seconds);
+ CU_ASSERT_NOT_EQUAL(
+ session.pcc_config.keep_alive_pce_negotiated_timer_seconds,
+ session.pcc_config.keep_alive_seconds);
+
+ pcep_msg_free_message(open_msg_corrected);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_keep_alive()
+{
+ /* Test when a Keep Alive is received, but the PCE Open has not been
+ * received yet */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_accepted = false;
+ session.pce_open_received = false;
+ session.pcc_open_accepted = false;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+
+ /* Test when a Keep Alive is received, and the PCE Open has been
+ * received and accepted */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_received = true;
+ session.pce_open_accepted = true;
+ session.pcc_open_accepted = false;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_TRUE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+
+ /* Test when a Keep Alive is received, and the PCE Open has been
+ * received and rejected */
+ create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false);
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+ session.timer_id_dead_timer = 100;
+ session.timer_id_open_keep_wait = 200;
+ session.pce_open_received = true;
+ session.pce_open_accepted = false;
+ session.pce_open_rejected = true;
+ session.pce_open_keep_alive_sent = false;
+ session.pcc_open_accepted = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_accepted);
+ CU_ASSERT_FALSE(session.pce_open_keep_alive_sent);
+ CU_ASSERT_FALSE(session.pcc_open_rejected);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET);
+ CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+
+ /* The session is considered connected, when both the
+ * PCE and PCC Open messages have been accepted */
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_pcrep()
+{
+ create_message_for_test(PCEP_TYPE_PCREP, false, true);
+ struct pcep_object_rp *rp =
+ pcep_obj_create_rp(1, true, true, true, true, 1, NULL);
+ dll_append(message->obj_list, rp);
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_pcreq()
+{
+ create_message_for_test(PCEP_TYPE_PCREQ, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* The PCC does not support receiving PcReq messages, so an error should
+ * be sent */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *error_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(error_msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries);
+ struct pcep_object_error *obj = error_msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value);
+ pcep_msg_free_message(error_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_report()
+{
+ create_message_for_test(PCEP_TYPE_REPORT, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* The PCC does not support receiving Report messages, so an error
+ * should be sent */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *error_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(error_msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries);
+ struct pcep_object_error *obj = error_msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value);
+ pcep_msg_free_message(error_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_handle_socket_comm_event_update()
+{
+ create_message_for_test(PCEP_TYPE_UPDATE, false, true);
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ double_linked_list *ero_subobj_list = dll_initialize();
+ dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102));
+ struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list);
+ struct pcep_object_metric *metric =
+ pcep_obj_create_metric(PCEP_METRIC_TE, false, true, 16.0);
+ dll_append(message->obj_list, srp);
+ dll_append(message->obj_list, lsp);
+ dll_append(message->obj_list, ero);
+ dll_append(message->obj_list, metric);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_UPDATE, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_initiate()
+{
+ create_message_for_test(PCEP_TYPE_INITIATE, false, true);
+ struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL);
+ struct pcep_object_lsp *lsp =
+ pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true,
+ true, true, true, NULL);
+ dll_append(message->obj_list, srp);
+ dll_append(message->obj_list, lsp);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_INITIATE, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_notify()
+{
+ create_message_for_test(PCEP_TYPE_PCNOTF, false, true);
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_PCNOTF, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_error()
+{
+ create_message_for_test(PCEP_TYPE_ERROR, false, true);
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
+
+
+void test_handle_socket_comm_event_unknown_msg()
+{
+ create_message_for_test(13, false, false);
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ /* Sending an unsupported message type, so an error should be sent,
+ * but the connection should remain open, since max_unknown_messages = 2
+ */
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 0);
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_error *error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+ destroy_message_for_test();
+
+ /* Send another unsupported message type, an error should be sent and
+ * the connection should be closed, since max_unknown_messages = 2 */
+ create_message_for_test(13, false, false);
+ reset_mock_socket_comm_info();
+ mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+
+ handle_socket_comm_event(&event);
+
+ verify_socket_comm_times_called(0, 0, 0, 2, 1, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_MAX_UNKOWN_MSGS, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ /* Verify the error message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ error_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED,
+ error_obj->error_type);
+ CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the Close message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(msg);
+ CU_ASSERT_EQUAL(PCEP_TYPE_CLOSE, msg->msg_header->type);
+ /* Verify the error object */
+ CU_ASSERT_EQUAL(1, msg->obj_list->num_entries);
+ struct pcep_object_close *close_obj = msg->obj_list->head->data;
+ CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_CLOSE, close_obj->header.object_class);
+ CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_CLOSE, close_obj->header.object_type);
+ CU_ASSERT_EQUAL(PCEP_CLOSE_REASON_UNREC_MSG, close_obj->reason);
+ pcep_msg_free_message(msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+}
+
+
+void test_connection_failure(void)
+{
+ /*
+ * Test when 2 invalid Open messages are received that a
+ * PCC_CONNECTION_FAILURE event is generated.
+ */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ struct pcep_object_open *open_object =
+ pcep_obj_create_open(1, 1, 1, NULL);
+ /* Make the Open message invalid */
+ open_object->open_deadtimer =
+ session.pcc_config.max_dead_timer_seconds + 1;
+ dll_append(message->obj_list, open_object);
+ session.pce_open_received = false;
+ session.pce_open_accepted = false;
+ session.pce_open_rejected = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* An error response should be sent, rejecting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 1);
+ pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /* Send the same erroneous Open again */
+ create_message_for_test(PCEP_TYPE_OPEN, false, false);
+ reset_mock_socket_comm_info();
+ open_object = pcep_obj_create_open(1, 1, 1, NULL);
+ /* Make the Open message invalid */
+ open_object->open_deadtimer =
+ session.pcc_config.max_dead_timer_seconds + 1;
+ dll_append(message->obj_list, open_object);
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pce_open_received);
+ CU_ASSERT_TRUE(session.pce_open_rejected);
+ CU_ASSERT_FALSE(session.pce_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ /* An error response should be sent, rejecting the Open */
+ verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+
+ destroy_message_for_test();
+
+ /*
+ * Test when 2 invalid Open messages are sent that a
+ * PCC_CONNECTION_FAILURE event is generated.
+ */
+ create_message_for_test(PCEP_TYPE_ERROR, false, false);
+ reset_mock_socket_comm_info();
+ struct pcep_object_error *error_object = pcep_obj_create_error(
+ PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG);
+ dll_append(message->obj_list, error_object);
+ session.pcc_open_accepted = false;
+ session.pcc_open_rejected = false;
+ session.session_state = SESSION_STATE_PCEP_CONNECTING;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pcc_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING);
+ /* Another Open should be sent */
+ verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type);
+ CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ destroy_message_for_test();
+
+ /* Send a socket close while connecting, which should
+ * generate a PCC_CONNECTION_FAILURE event */
+ reset_mock_socket_comm_info();
+ event.socket_closed = true;
+ event.received_msg_list = NULL;
+
+ handle_socket_comm_event(&event);
+
+ CU_ASSERT_TRUE(session.pcc_open_rejected);
+ CU_ASSERT_FALSE(session.pcc_open_accepted);
+ CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED);
+ verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0);
+ CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries,
+ 2);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+ e = queue_dequeue(session_logic_event_queue_->event_queue);
+ CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type);
+ pceplib_free(PCEPLIB_INFRA, e);
+}
diff --git a/pceplib/test/pcep_session_logic_states_test.h b/pceplib/test/pcep_session_logic_states_test.h
new file mode 100644
index 0000000000..e42b501ed9
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_states_test.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_STATES_TEST_H
+#define PCEP_SESSION_LOGIC_STATES_TEST_H
+
+int pcep_session_logic_states_test_suite_setup(void);
+int pcep_session_logic_states_test_suite_teardown(void);
+void pcep_session_logic_states_test_setup(void);
+void pcep_session_logic_states_test_teardown(void);
+void test_handle_timer_event_dead_timer(void);
+void test_handle_timer_event_keep_alive(void);
+void test_handle_timer_event_open_keep_wait(void);
+void test_handle_socket_comm_event_null_params(void);
+void test_handle_socket_comm_event_close(void);
+void test_handle_socket_comm_event_open(void);
+void test_handle_socket_comm_event_open_error(void);
+void test_handle_socket_comm_event_keep_alive(void);
+void test_handle_socket_comm_event_pcrep(void);
+void test_handle_socket_comm_event_pcreq(void);
+void test_handle_socket_comm_event_report(void);
+void test_handle_socket_comm_event_update(void);
+void test_handle_socket_comm_event_initiate(void);
+void test_handle_socket_comm_event_notify(void);
+void test_handle_socket_comm_event_error(void);
+void test_handle_socket_comm_event_unknown_msg(void);
+void test_connection_failure(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_test.c b/pceplib/test/pcep_session_logic_test.c
new file mode 100644
index 0000000000..66db4fbaea
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_test.c
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_mock.h"
+#include "pcep_session_logic.h"
+#include "pcep_session_logic_test.h"
+
+/*
+ * Test suite setup and teardown called before AND after the test suite.
+ */
+
+int pcep_session_logic_test_suite_setup(void)
+{
+ pceplib_memory_reset();
+ return 0;
+}
+
+int pcep_session_logic_test_suite_teardown(void)
+{
+ printf("\n");
+ pceplib_memory_dump();
+ return 0;
+}
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+
+void pcep_session_logic_test_setup()
+{
+ setup_mock_socket_comm_info();
+}
+
+
+void pcep_session_logic_test_teardown()
+{
+ stop_session_logic();
+ teardown_mock_socket_comm_info();
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_run_stop_session_logic()
+{
+ CU_ASSERT_TRUE(run_session_logic());
+ CU_ASSERT_TRUE(stop_session_logic());
+}
+
+
+void test_run_session_logic_twice()
+{
+ CU_ASSERT_TRUE(run_session_logic());
+ CU_ASSERT_FALSE(run_session_logic());
+}
+
+
+void test_session_logic_without_run()
+{
+ /* Verify the functions that depend on run_session_logic() being called
+ */
+ CU_ASSERT_FALSE(stop_session_logic());
+}
+
+
+void test_create_pcep_session_null_params()
+{
+ pcep_configuration config;
+ struct in_addr pce_ip;
+
+ CU_ASSERT_PTR_NULL(create_pcep_session(NULL, NULL));
+ CU_ASSERT_PTR_NULL(create_pcep_session(NULL, &pce_ip));
+ CU_ASSERT_PTR_NULL(create_pcep_session(&config, NULL));
+}
+
+
+void test_create_destroy_pcep_session()
+{
+ pcep_session *session;
+ pcep_configuration config;
+ struct in_addr pce_ip;
+
+ run_session_logic();
+
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.keep_alive_seconds = 5;
+ config.dead_timer_seconds = 5;
+ config.request_time_seconds = 5;
+ config.max_unknown_messages = 5;
+ config.max_unknown_requests = 5;
+ inet_pton(AF_INET, "127.0.0.1", &(pce_ip));
+
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Should be an Open, with no TLVs: length = 12 */
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_create_destroy_pcep_session_ipv6()
+{
+ pcep_session *session;
+ pcep_configuration config;
+ struct in6_addr pce_ip;
+
+ run_session_logic();
+
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.keep_alive_seconds = 5;
+ config.dead_timer_seconds = 5;
+ config.request_time_seconds = 5;
+ config.max_unknown_messages = 5;
+ config.max_unknown_requests = 5;
+ config.is_src_ipv6 = true;
+ inet_pton(AF_INET6, "::1", &pce_ip);
+
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ session = create_pcep_session_ipv6(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6);
+ /* What gets saved in the mock is the msg byte buffer. The msg struct
+ * was deleted when it was sent. Instead of inspecting the msg byte
+ * buffer, lets just decode it. */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ struct pcep_message *open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Should be an Open, with no TLVs: length = 12 */
+ CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN);
+ CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_create_pcep_session_open_tlvs()
+{
+ pcep_session *session;
+ struct in_addr pce_ip;
+ struct pcep_message *open_msg;
+ struct pcep_object_header *open_obj;
+ pcep_configuration config;
+ memset(&config, 0, sizeof(pcep_configuration));
+ config.pcep_msg_versioning = create_default_pcep_versioning();
+ inet_pton(AF_INET, "127.0.0.1", &(pce_ip));
+
+ run_session_logic();
+
+ /* Verify the created Open message only has 1 TLV:
+ * pcep_tlv_create_stateful_pce_capability() */
+ mock_socket_comm_info *mock_info = get_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_stateful_pce_lsp_update = true;
+ config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = false;
+ config.support_sr_te_pst = false;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ uint8_t *encoded_msg =
+ dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 1);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the created Open message only has 2 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_include_db_version = true;
+ config.lsp_db_version = 100;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 2);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)
+ open_obj->tlv_list->head->next_node->data)
+ ->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+
+ /* Verify the created Open message only has 4 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version()
+ * pcep_tlv_create_sr_pce_capability()
+ * pcep_tlv_create_path_setup_type_capability() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.support_sr_te_pst = true;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 3);
+ double_linked_list_node *tlv_node = open_obj->tlv_list->head;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ /* Verify the created Open message only has 4 TLVs:
+ * pcep_tlv_create_stateful_pce_capability()
+ * pcep_tlv_create_lsp_db_version()
+ * pcep_tlv_create_sr_pce_capability()
+ * pcep_tlv_create_path_setup_type_capability() */
+ reset_mock_socket_comm_info();
+ mock_info->send_message_save_message = true;
+ config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true;
+
+ session = create_pcep_session(&config, &pce_ip);
+ CU_ASSERT_PTR_NOT_NULL(session);
+ /* Get and verify the Open Message */
+ encoded_msg = dll_delete_first_node(mock_info->sent_message_list);
+ CU_ASSERT_PTR_NOT_NULL(encoded_msg);
+ open_msg = pcep_decode_message(encoded_msg);
+ CU_ASSERT_PTR_NOT_NULL(open_msg);
+ /* Get and verify the Open Message objects */
+ CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list);
+ CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0);
+ /* Get and verify the Open object */
+ open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN);
+ CU_ASSERT_PTR_NOT_NULL(open_obj);
+ /* Get and verify the Open object TLVs */
+ CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list);
+ CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 4);
+ tlv_node = open_obj->tlv_list->head;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY);
+ tlv_node = tlv_node->next_node;
+ CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type,
+ PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY);
+
+ destroy_pcep_versioning(config.pcep_msg_versioning);
+ destroy_pcep_session(session);
+ pcep_msg_free_message(open_msg);
+ pceplib_free(PCEPLIB_MESSAGES, encoded_msg);
+
+ stop_session_logic();
+}
+
+
+void test_destroy_pcep_session_null_session()
+{
+ /* Just testing that it does not core dump */
+ destroy_pcep_session(NULL);
+}
diff --git a/pceplib/test/pcep_session_logic_test.h b/pceplib/test/pcep_session_logic_test.h
new file mode 100644
index 0000000000..6cc1963250
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SESSION_LOGIC_TEST_H_
+#define PCEP_SESSION_LOGIC_TEST_H_
+
+int pcep_session_logic_test_suite_setup(void);
+int pcep_session_logic_test_suite_teardown(void);
+void pcep_session_logic_test_setup(void);
+void pcep_session_logic_test_teardown(void);
+void test_run_stop_session_logic(void);
+void test_run_session_logic_twice(void);
+void test_session_logic_without_run(void);
+void test_create_pcep_session_null_params(void);
+void test_create_destroy_pcep_session(void);
+void test_create_destroy_pcep_session_ipv6(void);
+void test_create_pcep_session_open_tlvs(void);
+void test_destroy_pcep_session_null_session(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_session_logic_tests.c b/pceplib/test/pcep_session_logic_tests.c
new file mode 100644
index 0000000000..67bf6e22ef
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_tests.c
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_session_logic_loop_test.h"
+#include "pcep_session_logic_states_test.h"
+#include "pcep_session_logic_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_session_logic_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic Test Suite",
+ pcep_session_logic_test_suite_setup, // suite setup and
+ // cleanup function
+ // pointers
+ pcep_session_logic_test_suite_teardown,
+ pcep_session_logic_test_setup, // test case setup
+ // function pointer
+ pcep_session_logic_test_teardown); // test case teardown
+ // function pointer
+
+ CU_add_test(test_session_logic_suite, "test_run_stop_session_logic",
+ test_run_stop_session_logic);
+ CU_add_test(test_session_logic_suite, "test_run_session_logic_twice",
+ test_run_session_logic_twice);
+ CU_add_test(test_session_logic_suite, "test_session_logic_without_run",
+ test_session_logic_without_run);
+ CU_add_test(test_session_logic_suite,
+ "test_create_pcep_session_null_params",
+ test_create_pcep_session_null_params);
+ CU_add_test(test_session_logic_suite,
+ "test_create_destroy_pcep_session",
+ test_create_destroy_pcep_session);
+ CU_add_test(test_session_logic_suite,
+ "test_create_destroy_pcep_session_ipv6",
+ test_create_destroy_pcep_session_ipv6);
+ CU_add_test(test_session_logic_suite,
+ "test_create_pcep_session_open_tlvs",
+ test_create_pcep_session_open_tlvs);
+ CU_add_test(test_session_logic_suite,
+ "test_destroy_pcep_session_null_session",
+ test_destroy_pcep_session_null_session);
+
+ CU_pSuite test_session_logic_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic Loop Test Suite",
+ pcep_session_logic_loop_test_suite_setup, // suite setup
+ // and cleanup
+ // function
+ // pointers
+ pcep_session_logic_loop_test_suite_teardown,
+ pcep_session_logic_loop_test_setup, // test case setup
+ // function pointer
+ pcep_session_logic_loop_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_loop_null_data",
+ test_session_logic_loop_null_data);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_loop_inactive",
+ test_session_logic_loop_inactive);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_msg_ready_handler",
+ test_session_logic_msg_ready_handler);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_conn_except_notifier",
+ test_session_logic_conn_except_notifier);
+ CU_add_test(test_session_logic_loop_suite,
+ "test_session_logic_timer_expire_handler",
+ test_session_logic_timer_expire_handler);
+
+ CU_pSuite test_session_logic_states_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Session Logic States Test Suite",
+ pcep_session_logic_states_test_suite_setup, // suite
+ // setup and
+ // cleanup
+ // function
+ // pointers
+ pcep_session_logic_states_test_suite_teardown,
+ pcep_session_logic_states_test_setup, // test case setup
+ // function
+ // pointer
+ pcep_session_logic_states_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_dead_timer",
+ test_handle_timer_event_dead_timer);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_keep_alive",
+ test_handle_timer_event_keep_alive);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_timer_event_open_keep_wait",
+ test_handle_timer_event_open_keep_wait);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_null_params",
+ test_handle_socket_comm_event_null_params);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_close",
+ test_handle_socket_comm_event_close);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_open",
+ test_handle_socket_comm_event_open);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_open_error",
+ test_handle_socket_comm_event_open_error);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_keep_alive",
+ test_handle_socket_comm_event_keep_alive);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_pcrep",
+ test_handle_socket_comm_event_pcrep);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_pcreq",
+ test_handle_socket_comm_event_pcreq);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_report",
+ test_handle_socket_comm_event_report);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_update",
+ test_handle_socket_comm_event_update);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_initiate",
+ test_handle_socket_comm_event_initiate);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_notify",
+ test_handle_socket_comm_event_notify);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_error",
+ test_handle_socket_comm_event_error);
+ CU_add_test(test_session_logic_states_suite,
+ "test_handle_socket_comm_event_unknown_msg",
+ test_handle_socket_comm_event_unknown_msg);
+ CU_add_test(test_session_logic_states_suite, "test_connection_failure",
+ test_connection_failure);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_session_logic_tests_valgrind.sh b/pceplib/test/pcep_session_logic_tests_valgrind.sh
new file mode 100755
index 0000000000..435bb3d5c4
--- /dev/null
+++ b/pceplib/test/pcep_session_logic_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_session_logic_tests
diff --git a/pceplib/test/pcep_socket_comm_loop_test.c b/pceplib/test/pcep_socket_comm_loop_test.c
new file mode 100644
index 0000000000..94f0983ca7
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_loop_test.c
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_loop.h"
+#include "pcep_socket_comm_loop_test.h"
+#include "pcep_socket_comm.h"
+#include "pcep_utils_memory.h"
+
+void test_loop_conn_except_notifier(void *session_data, int socket_fd);
+
+/*
+ * Functions to be tested, implemented in pcep_socket_comm_loop.c
+ */
+
+typedef struct ready_to_read_handler_info_ {
+ bool handler_called;
+ bool except_handler_called;
+ void *data;
+ int socket_fd;
+ int bytes_read;
+
+} ready_to_read_handler_info;
+
+static ready_to_read_handler_info read_handler_info;
+static pcep_socket_comm_session *test_comm_session;
+static pcep_socket_comm_handle *test_socket_comm_handle = NULL;
+
+static int test_loop_message_ready_to_read_handler(void *session_data,
+ int socket_fd)
+{
+ read_handler_info.handler_called = true;
+ read_handler_info.data = session_data;
+ read_handler_info.socket_fd = socket_fd;
+
+ return read_handler_info.bytes_read;
+}
+
+
+void test_loop_conn_except_notifier(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ read_handler_info.except_handler_called = true;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+void pcep_socket_comm_loop_test_setup()
+{
+ test_socket_comm_handle =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle));
+ memset(test_socket_comm_handle, 0, sizeof(pcep_socket_comm_handle));
+ test_socket_comm_handle->active = false;
+ test_socket_comm_handle->read_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ test_socket_comm_handle->write_list =
+ ordered_list_initialize(socket_fd_node_compare);
+ test_socket_comm_handle->session_list =
+ ordered_list_initialize(pointer_compare_function);
+ pthread_mutex_init(&test_socket_comm_handle->socket_comm_mutex, NULL);
+ test_socket_comm_handle->num_active_sessions = 0;
+
+ test_comm_session =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session));
+ memset(test_comm_session, 0, sizeof(pcep_socket_comm_session));
+ test_comm_session->message_ready_to_read_handler =
+ test_loop_message_ready_to_read_handler;
+ ordered_list_add_node(test_socket_comm_handle->session_list,
+ test_comm_session);
+
+ read_handler_info.handler_called = false;
+ read_handler_info.except_handler_called = false;
+ read_handler_info.data = NULL;
+ read_handler_info.socket_fd = -1;
+ read_handler_info.bytes_read = 0;
+}
+
+
+void pcep_socket_comm_loop_test_teardown()
+{
+ pthread_mutex_destroy(&test_socket_comm_handle->socket_comm_mutex);
+ ordered_list_destroy(test_socket_comm_handle->read_list);
+ ordered_list_destroy(test_socket_comm_handle->write_list);
+ ordered_list_destroy(test_socket_comm_handle->session_list);
+ pceplib_free(PCEPLIB_INFRA, test_socket_comm_handle);
+ test_socket_comm_handle = NULL;
+
+ if (test_comm_session != NULL) {
+ pceplib_free(PCEPLIB_INFRA, test_comm_session);
+ test_comm_session = NULL;
+ }
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_socket_comm_loop_null_handle()
+{
+ /* Verify that socket_comm_loop() correctly handles a NULL
+ * timers_context */
+ socket_comm_loop(NULL);
+}
+
+
+void test_socket_comm_loop_not_active()
+{
+ /* Verify that event_loop() correctly handles an inactive flag */
+ pcep_socket_comm_handle handle;
+ handle.active = false;
+ socket_comm_loop(&handle);
+}
+
+
+void test_handle_reads_no_read()
+{
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_FALSE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+}
+
+
+void test_handle_reads_read_message()
+{
+ /* Setup the comm session so that it can read.
+ * It should read 100 bytes, which simulates a successful read */
+ test_comm_session->socket_fd = 10;
+ read_handler_info.bytes_read = 100;
+ FD_SET(test_comm_session->socket_fd,
+ &test_socket_comm_handle->read_master_set);
+ ordered_list_add_node(test_socket_comm_handle->read_list,
+ test_comm_session);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_TRUE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_EQUAL(test_comm_session->received_bytes,
+ read_handler_info.bytes_read);
+}
+
+
+void test_handle_reads_read_message_close()
+{
+ /* Setup the comm session so that it can read.
+ * It should read 0 bytes, which simulates that the socket closed */
+ test_comm_session->socket_fd = 11;
+ read_handler_info.bytes_read = 0;
+ FD_SET(test_comm_session->socket_fd,
+ &test_socket_comm_handle->read_master_set);
+ ordered_list_add_node(test_socket_comm_handle->read_list,
+ test_comm_session);
+
+ handle_reads(test_socket_comm_handle);
+
+ CU_ASSERT_TRUE(read_handler_info.handler_called);
+ CU_ASSERT_FALSE(read_handler_info.except_handler_called);
+ CU_ASSERT_EQUAL(test_comm_session->received_bytes,
+ read_handler_info.bytes_read);
+ CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head);
+}
diff --git a/pceplib/test/pcep_socket_comm_loop_test.h b/pceplib/test/pcep_socket_comm_loop_test.h
new file mode 100644
index 0000000000..d2e3f21ee1
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_loop_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SOCKET_COMM_LOOP_TEST_H_
+#define PCEP_SOCKET_COMM_LOOP_TEST_H_
+
+void pcep_socket_comm_loop_test_setup(void);
+void pcep_socket_comm_loop_test_teardown(void);
+void test_socket_comm_loop_null_handle(void);
+void test_socket_comm_loop_not_active(void);
+void test_handle_reads_no_read(void);
+void test_handle_reads_read_message(void);
+void test_handle_reads_read_message_close(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_socket_comm_test.c b/pceplib/test/pcep_socket_comm_test.c
new file mode 100644
index 0000000000..35afbcbb13
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_test.c
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <netinet/in.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_socket_comm.h"
+#include "pcep_socket_comm_internals.h"
+#include "pcep_socket_comm_test.h"
+
+extern pcep_socket_comm_handle *socket_comm_handle_;
+
+static pcep_socket_comm_session *test_session = NULL;
+static struct in_addr test_host_ip;
+static struct in_addr test_src_ip;
+static struct in6_addr test_host_ipv6;
+static struct in6_addr test_src_ipv6;
+static short test_port = 4789;
+static short test_src_port = 4999;
+static uint32_t connect_timeout_millis = 500;
+
+/*
+ * Unit Test Basic pcep_socket_comm API usage.
+ * Testing sending messages, etc via sockets should be done
+ * with integration tests, not unit tests.
+ */
+
+/*
+ * Different socket_comm handler test implementations
+ */
+static void test_message_received_handler(void *session_data,
+ const char *message_data,
+ unsigned int message_length)
+{
+ (void)session_data;
+ (void)message_data;
+ (void)message_length;
+}
+
+static int test_message_ready_to_read_handler(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ return 1;
+}
+
+static void test_message_sent_handler(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+ return;
+}
+
+static void test_connection_except_notifier(void *session_data, int socket_fd)
+{
+ (void)session_data;
+ (void)socket_fd;
+}
+
+
+/*
+ * Test case setup and teardown called before AND after each test.
+ */
+void pcep_socket_comm_test_setup()
+{
+ inet_pton(AF_INET, "127.0.0.1", &(test_host_ip));
+ inet_pton(AF_INET, "127.0.0.1", &(test_src_ip));
+ inet_pton(AF_INET6, "::1", &(test_host_ipv6));
+ inet_pton(AF_INET6, "::1", &(test_src_ipv6));
+}
+
+void pcep_socket_comm_test_teardown()
+{
+ socket_comm_session_teardown(test_session);
+ test_session = NULL;
+}
+
+
+/*
+ * Test cases
+ */
+
+void test_pcep_socket_comm_initialize()
+{
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_ipv6()
+{
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_with_src()
+{
+ /* Test that INADDR_ANY will be used when src_ip is NULL */
+ test_session = socket_comm_session_initialize_with_src(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, NULL, 0, &test_host_ip,
+ test_port, connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr,
+ INADDR_ANY);
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_with_src(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_src_ip, test_src_port,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr,
+ test_src_ip.s_addr);
+ CU_ASSERT_EQUAL(test_session->src_sock_addr.src_sock_addr_ipv4.sin_port,
+ ntohs(test_src_port));
+ CU_ASSERT_FALSE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_with_src_ipv6()
+{
+ /* Test that INADDR6_ANY will be used when src_ip is NULL */
+ test_session = socket_comm_session_initialize_with_src_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, NULL, 0, &test_host_ipv6,
+ test_port, connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ &in6addr_any, sizeof(struct in6_addr)),
+ 0);
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_with_src_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_src_ipv6, test_src_port,
+ &test_host_ipv6, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6
+ .sin6_addr,
+ &test_src_ipv6, sizeof(struct in6_addr)),
+ 0);
+ CU_ASSERT_EQUAL(
+ test_session->src_sock_addr.src_sock_addr_ipv6.sin6_port,
+ ntohs(test_src_port));
+ CU_ASSERT_TRUE(test_session->is_ipv6);
+}
+
+
+void test_pcep_socket_comm_initialize_tcpmd5()
+{
+ char tcp_md5_str[] = "hello";
+ int tcp_md5_strlen = strlen(tcp_md5_str);
+
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port, 1,
+ tcp_md5_str, true, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_TRUE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+ /* This call does not work, it returns errno=92, Protocol not available
+ getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig,
+ &siglen);*/
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ip, test_port, 1,
+ tcp_md5_str, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_FALSE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+}
+
+
+void test_pcep_socket_comm_initialize_ipv6_tcpmd5()
+{
+ char tcp_md5_str[] = "hello";
+ int tcp_md5_strlen = strlen(tcp_md5_str);
+
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port, 1,
+ tcp_md5_str, true, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_TRUE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+ /* This call does not work, it returns errno=92, Protocol not available
+ getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig,
+ &siglen);*/
+
+ socket_comm_session_teardown(test_session);
+ test_session = socket_comm_session_initialize_ipv6(
+ test_message_received_handler, NULL, NULL,
+ test_connection_except_notifier, &test_host_ipv6, test_port, 1,
+ tcp_md5_str, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str,
+ test_session->tcp_authentication_str,
+ tcp_md5_strlen));
+ CU_ASSERT_FALSE(test_session->is_tcp_auth_md5);
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session));
+}
+
+
+void test_pcep_socket_comm_initialize_handlers()
+{
+ /* Verify incorrect handler usage is correctly handled */
+
+ /* Both receive handlers cannot be NULL */
+ test_session = socket_comm_session_initialize(
+ NULL, NULL, NULL, test_connection_except_notifier,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NULL(test_session);
+
+ /* Both receive handlers cannot be set */
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler,
+ test_message_ready_to_read_handler, test_message_sent_handler,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NULL(test_session);
+
+ /* Only one receive handler can be set */
+ test_session = socket_comm_session_initialize(
+ NULL, test_message_ready_to_read_handler,
+ test_message_sent_handler, test_connection_except_notifier,
+ &test_host_ip, test_port, connect_timeout_millis, NULL, false,
+ NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+}
+
+
+void test_pcep_socket_comm_session_not_initialized()
+{
+ CU_ASSERT_FALSE(socket_comm_session_connect_tcp(NULL));
+ CU_ASSERT_FALSE(socket_comm_session_close_tcp(NULL));
+ CU_ASSERT_FALSE(socket_comm_session_close_tcp_after_write(NULL));
+ socket_comm_session_send_message(NULL, NULL, 0, true);
+ CU_ASSERT_FALSE(socket_comm_session_teardown(NULL));
+}
+
+
+void test_pcep_socket_comm_session_destroy()
+{
+ test_session = socket_comm_session_initialize(
+ test_message_received_handler, NULL, test_message_sent_handler,
+ test_connection_except_notifier, &test_host_ip, test_port,
+ connect_timeout_millis, NULL, false, NULL);
+ CU_ASSERT_PTR_NOT_NULL(test_session);
+ CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_);
+ CU_ASSERT_EQUAL(socket_comm_handle_->num_active_sessions, 1);
+
+ CU_ASSERT_TRUE(socket_comm_session_teardown(test_session));
+ test_session = NULL;
+ CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_);
+
+ CU_ASSERT_TRUE(destroy_socket_comm_loop());
+ CU_ASSERT_PTR_NULL(socket_comm_handle_);
+}
diff --git a/pceplib/test/pcep_socket_comm_test.h b/pceplib/test/pcep_socket_comm_test.h
new file mode 100644
index 0000000000..f857af087a
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_test.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_SOCKET_COMM_TEST_H_
+#define PCEP_SOCKET_COMM_TEST_H_
+
+void pcep_socket_comm_test_teardown(void);
+void pcep_socket_comm_test_setup(void);
+void test_pcep_socket_comm_initialize(void);
+void test_pcep_socket_comm_initialize_ipv6(void);
+void test_pcep_socket_comm_initialize_with_src(void);
+void test_pcep_socket_comm_initialize_with_src_ipv6(void);
+void test_pcep_socket_comm_initialize_tcpmd5(void);
+void test_pcep_socket_comm_initialize_ipv6_tcpmd5(void);
+void test_pcep_socket_comm_initialize_handlers(void);
+void test_pcep_socket_comm_session_not_initialized(void);
+void test_pcep_socket_comm_session_destroy(void);
+
+#endif
diff --git a/pceplib/test/pcep_socket_comm_tests.c b/pceplib/test/pcep_socket_comm_tests.c
new file mode 100644
index 0000000000..293678f1a7
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_tests.c
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_socket_comm_loop_test.h"
+#include "pcep_socket_comm_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_socket_comm_test.c
+ */
+ CU_pSuite test_socket_comm_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Socket Comm Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ pcep_socket_comm_test_setup, // test case setup function pointer
+ pcep_socket_comm_test_teardown); // test case teardown function
+ // pointer
+
+ CU_add_test(test_socket_comm_suite, "test_pcep_socket_comm_initialize",
+ test_pcep_socket_comm_initialize);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_ipv6",
+ test_pcep_socket_comm_initialize_ipv6);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_with_src",
+ test_pcep_socket_comm_initialize_with_src);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_with_src_ipv6",
+ test_pcep_socket_comm_initialize_with_src_ipv6);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_tcpmd5",
+ test_pcep_socket_comm_initialize_tcpmd5);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_ipv6_tcpmd5",
+ test_pcep_socket_comm_initialize_ipv6_tcpmd5);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_initialize_handlers",
+ test_pcep_socket_comm_initialize_handlers);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_session_not_initialized",
+ test_pcep_socket_comm_session_not_initialized);
+ CU_add_test(test_socket_comm_suite,
+ "test_pcep_socket_comm_session_destroy",
+ test_pcep_socket_comm_session_destroy);
+
+ /*
+ * Tests defined in pcep_socket_comm_loop_test.c
+ */
+ CU_pSuite test_socket_comm_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Socket Comm Loop Test Suite", NULL, NULL,
+ pcep_socket_comm_loop_test_setup, // suite setup
+ // function pointer
+ pcep_socket_comm_loop_test_teardown); // suite cleanup
+ // function
+ // pointer
+
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_socket_comm_loop_null_handle",
+ test_socket_comm_loop_null_handle);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_socket_comm_loop_not_active",
+ test_socket_comm_loop_not_active);
+ CU_add_test(test_socket_comm_loop_suite, "test_handle_reads_no_read",
+ test_handle_reads_no_read);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_handle_reads_read_message",
+ test_handle_reads_read_message);
+ CU_add_test(test_socket_comm_loop_suite,
+ "test_handle_reads_read_message_close",
+ test_handle_reads_read_message_close);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_socket_comm_tests_valgrind.sh b/pceplib/test/pcep_socket_comm_tests_valgrind.sh
new file mode 100755
index 0000000000..d9e95e4c1c
--- /dev/null
+++ b/pceplib/test/pcep_socket_comm_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_socket_comm_tests
diff --git a/pceplib/test/pcep_tests_valgrind.sh b/pceplib/test/pcep_tests_valgrind.sh
new file mode 100755
index 0000000000..ca4772cb67
--- /dev/null
+++ b/pceplib/test/pcep_tests_valgrind.sh
@@ -0,0 +1,15 @@
+#
+# Common function definition for PCEPlib valgrind tests
+#
+
+function valgrind_test()
+{
+ local test_suite=$1
+ [[ -z ${test_suite} ]] && { echo "${FUNCNAME}(): test_suite not specified."; exit 1; }
+ [[ ! -x "${test_suite}" ]] && { echo "${test_suite} is not an executable file."; exit 1; }
+
+ G_SLICE=always-malloc
+ G_DEBUG=gc-friendly
+ VALGRIND="valgrind -v --tool=memcheck --leak-check=full --num-callers=40 --error-exitcode=1"
+ ${VALGRIND} --log-file=${test_suite}.val.log ./${test_suite} || ({ echo "Valgrind memory check error"; exit 1; })
+}
diff --git a/pceplib/test/pcep_timers_event_loop_test.c b/pceplib/test/pcep_timers_event_loop_test.c
new file mode 100644
index 0000000000..9fcacaf0f2
--- /dev/null
+++ b/pceplib/test/pcep_timers_event_loop_test.c
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_timers.h"
+#include "pcep_utils_memory.h"
+#include "pcep_timers_event_loop.h"
+#include "pcep_timers_event_loop_test.h"
+
+
+typedef struct timer_expire_handler_info_ {
+ bool handler_called;
+ void *data;
+ int timerId;
+
+} timer_expire_handler_info;
+
+static pcep_timers_context *test_timers_context = NULL;
+static timer_expire_handler_info expire_handler_info;
+#define TEST_EVENT_LOOP_TIMER_ID 500
+
+
+/* Called when a timer expires */
+static void test_timer_expire_handler(void *data, int timerId)
+{
+ expire_handler_info.handler_called = true;
+ expire_handler_info.data = data;
+ expire_handler_info.timerId = timerId;
+}
+
+
+/* Test case setup called before each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_event_loop_test_setup()
+{
+ test_timers_context =
+ pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timers_context));
+ memset(test_timers_context, 0, sizeof(pcep_timers_context));
+ if (pthread_mutex_init(&(test_timers_context->timer_list_lock), NULL)
+ != 0) {
+ fprintf(stderr,
+ "ERROR initializing timers, cannot initialize the mutex\n");
+ }
+ test_timers_context->active = false;
+ test_timers_context->expire_handler = test_timer_expire_handler;
+ test_timers_context->timer_list =
+ ordered_list_initialize(timer_list_node_timer_id_compare);
+
+ expire_handler_info.handler_called = false;
+ expire_handler_info.data = NULL;
+ expire_handler_info.timerId = -1;
+}
+
+
+/* Test case teardown called after each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_event_loop_test_teardown()
+{
+ pthread_mutex_unlock(&test_timers_context->timer_list_lock);
+ pthread_mutex_destroy(&(test_timers_context->timer_list_lock));
+ ordered_list_destroy(test_timers_context->timer_list);
+ pceplib_free(PCEPLIB_INFRA, test_timers_context);
+ test_timers_context = NULL;
+}
+
+
+/*
+ * Test functions
+ */
+
+void test_walk_and_process_timers_no_timers()
+{
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+
+ walk_and_process_timers(test_timers_context);
+
+ CU_ASSERT_FALSE(expire_handler_info.handler_called);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+}
+
+
+void test_walk_and_process_timers_timer_not_expired()
+{
+ pcep_timer timer;
+ timer.data = &timer;
+ // Set the timer to expire 100 seconds from now
+ timer.expire_time = time(NULL) + 100;
+ timer.timer_id = TEST_EVENT_LOOP_TIMER_ID;
+ ordered_list_add_node(test_timers_context->timer_list, &timer);
+
+ walk_and_process_timers(test_timers_context);
+
+ /* The timer should still be in the list, since it hasnt expired yet */
+ CU_ASSERT_FALSE(expire_handler_info.handler_called);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 1);
+ CU_ASSERT_PTR_NOT_NULL(test_timers_context->timer_list->head);
+}
+
+
+void test_walk_and_process_timers_timer_expired()
+{
+ /* We need to alloc it, since it will be free'd in
+ * walk_and_process_timers */
+ pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer));
+ timer->data = timer;
+ // Set the timer to expire 10 seconds ago
+ timer->expire_time = time(NULL) - 10;
+ timer->timer_id = TEST_EVENT_LOOP_TIMER_ID;
+ ordered_list_add_node(test_timers_context->timer_list, timer);
+
+ walk_and_process_timers(test_timers_context);
+
+ /* Since the timer expired, the expire_handler should have been called
+ * and the timer should have been removed from the timer list */
+ CU_ASSERT_TRUE(expire_handler_info.handler_called);
+ CU_ASSERT_PTR_EQUAL(expire_handler_info.data, timer);
+ CU_ASSERT_EQUAL(expire_handler_info.timerId, TEST_EVENT_LOOP_TIMER_ID);
+ CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0);
+ CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head);
+}
+
+void test_event_loop_null_handle()
+{
+ /* Verify that event_loop() correctly handles a NULL timers_context */
+ event_loop(NULL);
+}
+
+
+void test_event_loop_not_active()
+{
+ /* Verify that event_loop() correctly handles an inactive timers_context
+ * flag */
+ test_timers_context->active = false;
+ event_loop(test_timers_context);
+}
diff --git a/pceplib/test/pcep_timers_event_loop_test.h b/pceplib/test/pcep_timers_event_loop_test.h
new file mode 100644
index 0000000000..19fd264858
--- /dev/null
+++ b/pceplib/test/pcep_timers_event_loop_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_EVENT_LOOP_TEST_H_
+#define PCEP_TIMERS_EVENT_LOOP_TEST_H_
+
+void pcep_timers_event_loop_test_setup(void);
+void pcep_timers_event_loop_test_teardown(void);
+void test_walk_and_process_timers_no_timers(void);
+void test_walk_and_process_timers_timer_not_expired(void);
+void test_walk_and_process_timers_timer_expired(void);
+void test_event_loop_null_handle(void);
+void test_event_loop_not_active(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_timers_test.c b/pceplib/test/pcep_timers_test.c
new file mode 100644
index 0000000000..9d9e0f6c1b
--- /dev/null
+++ b/pceplib/test/pcep_timers_test.c
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdbool.h>
+#include <CUnit/CUnit.h>
+
+#include "pcep_timers.h"
+#include "pcep_timers_test.h"
+
+/* Test case teardown called after each test.
+ * Declared in pcep_timers_tests.c */
+void pcep_timers_test_teardown()
+{
+ teardown_timers();
+}
+
+static void test_timer_expire_handler(void *data, int timerId)
+{
+ (void)data;
+ (void)timerId;
+}
+
+
+void test_double_initialization(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), false);
+}
+
+
+void test_initialization_null_callback(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(NULL), false);
+}
+
+
+void test_not_initialized(void)
+{
+ /* All of these should fail if initialize_timers() hasnt been called */
+ CU_ASSERT_EQUAL(create_timer(5, NULL), -1);
+ CU_ASSERT_EQUAL(cancel_timer(7), false);
+ CU_ASSERT_EQUAL(reset_timer(7), false);
+ CU_ASSERT_EQUAL(teardown_timers(), false);
+}
+
+
+void test_create_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(0, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+}
+
+
+void test_cancel_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(10, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+
+ CU_ASSERT_EQUAL(cancel_timer(timer_id), true);
+}
+
+
+void test_cancel_timer_invalid(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(cancel_timer(1), false);
+}
+
+
+void test_reset_timer(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+
+ int timer_id = create_timer(10, NULL);
+ CU_ASSERT_TRUE(timer_id > -1);
+
+ CU_ASSERT_EQUAL(reset_timer(timer_id), true);
+}
+
+
+void test_reset_timer_invalid(void)
+{
+ CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true);
+ CU_ASSERT_EQUAL(reset_timer(1), false);
+}
diff --git a/pceplib/test/pcep_timers_test.h b/pceplib/test/pcep_timers_test.h
new file mode 100644
index 0000000000..6ac9a90e49
--- /dev/null
+++ b/pceplib/test/pcep_timers_test.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_TIMERS_TEST_H_
+#define PCEP_TIMERS_TEST_H_
+
+void pcep_timers_test_teardown(void);
+void test_double_initialization(void);
+void test_initialization_null_callback(void);
+void test_not_initialized(void);
+void test_create_timer(void);
+void test_cancel_timer(void);
+void test_cancel_timer_invalid(void);
+void test_reset_timer(void);
+void test_reset_timer_invalid(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_timers_tests.c b/pceplib/test/pcep_timers_tests.c
new file mode 100644
index 0000000000..adfea17e29
--- /dev/null
+++ b/pceplib/test/pcep_timers_tests.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+
+#include "pcep_timers_test.h"
+#include "pcep_timers_event_loop_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ /*
+ * Tests defined in pcep_timers_test.c
+ */
+ CU_pSuite test_timers_suite = CU_add_suite_with_setup_and_teardown(
+ "PCEP Timers Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ NULL, pcep_timers_test_teardown); // test case setup and
+ // teardown function pointers
+ CU_add_test(test_timers_suite, "test_double_initialization",
+ test_double_initialization);
+ CU_add_test(test_timers_suite, "test_initialization_null_callback",
+ test_initialization_null_callback);
+ CU_add_test(test_timers_suite, "test_not_initialized",
+ test_not_initialized);
+ CU_add_test(test_timers_suite, "test_create_timer", test_create_timer);
+ CU_add_test(test_timers_suite, "test_cancel_timer", test_cancel_timer);
+ CU_add_test(test_timers_suite, "test_cancel_timer_invalid",
+ test_cancel_timer_invalid);
+ CU_add_test(test_timers_suite, "test_reset_timer", test_reset_timer);
+ CU_add_test(test_timers_suite, "test_reset_timer_invalid",
+ test_reset_timer_invalid);
+
+ /*
+ * Tests defined in pcep_timers_event_loop_test.c
+ */
+ CU_pSuite test_timers_event_loop_suite =
+ CU_add_suite_with_setup_and_teardown(
+ "PCEP Timers Event Loop Test Suite", NULL,
+ NULL, // suite setup and cleanup function pointers
+ pcep_timers_event_loop_test_setup, // test case setup
+ // function pointer
+ pcep_timers_event_loop_test_teardown); // test case
+ // teardown
+ // function
+ // pointer
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_no_timers",
+ test_walk_and_process_timers_no_timers);
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_timer_not_expired",
+ test_walk_and_process_timers_timer_not_expired);
+ CU_add_test(test_timers_event_loop_suite,
+ "test_walk_and_process_timers_timer_expired",
+ test_walk_and_process_timers_timer_expired);
+ CU_add_test(test_timers_event_loop_suite, "test_event_loop_null_handle",
+ test_event_loop_null_handle);
+ CU_add_test(test_timers_event_loop_suite, "test_event_loop_not_active",
+ test_event_loop_not_active);
+
+ /*
+ * Run the tests and cleanup.
+ */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_timers_tests_valgrind.sh b/pceplib/test/pcep_timers_tests_valgrind.sh
new file mode 100755
index 0000000000..f9bff3b2a6
--- /dev/null
+++ b/pceplib/test/pcep_timers_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_timers_tests
diff --git a/pceplib/test/pcep_utils_counters_test.c b/pceplib/test/pcep_utils_counters_test.c
new file mode 100644
index 0000000000..6f53e4d400
--- /dev/null
+++ b/pceplib/test/pcep_utils_counters_test.c
@@ -0,0 +1,254 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_counters.h"
+#include "pcep_utils_counters_test.h"
+
+
+void test_create_counters_group()
+{
+ const char group_name[] = "group";
+ uint16_t num_subgroups = 10;
+
+ struct counters_group *group =
+ create_counters_group(NULL, num_subgroups);
+ CU_ASSERT_PTR_NULL(group);
+
+ group = create_counters_group(group_name, MAX_COUNTER_GROUPS + 1);
+ CU_ASSERT_PTR_NULL(group);
+
+ group = create_counters_group(group_name, num_subgroups);
+ CU_ASSERT_PTR_NOT_NULL(group);
+
+ CU_ASSERT_EQUAL(group->num_subgroups, 0);
+ CU_ASSERT_EQUAL(group->max_subgroups, num_subgroups);
+ CU_ASSERT_EQUAL(strcmp(group->counters_group_name, group_name), 0);
+
+ delete_counters_group(group);
+}
+
+void test_create_counters_subgroup()
+{
+ const char subgroup_name[] = "subgroup";
+ uint16_t subgroup_id = 10;
+ uint16_t num_counters = 20;
+
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup(NULL, subgroup_id, num_counters);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(
+ subgroup_name, MAX_COUNTER_GROUPS + 1, num_counters);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(subgroup_name, subgroup_id,
+ MAX_COUNTERS + 1);
+ CU_ASSERT_PTR_NULL(subgroup);
+
+ subgroup = create_counters_subgroup(subgroup_name, subgroup_id,
+ num_counters);
+ CU_ASSERT_PTR_NOT_NULL(subgroup);
+
+ CU_ASSERT_EQUAL(subgroup->subgroup_id, subgroup_id);
+ CU_ASSERT_EQUAL(subgroup->num_counters, 0);
+ CU_ASSERT_EQUAL(subgroup->max_counters, num_counters);
+ CU_ASSERT_EQUAL(strcmp(subgroup->counters_subgroup_name, subgroup_name),
+ 0);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_add_counters_subgroup()
+{
+ struct counters_group *group = create_counters_group("group", 1);
+ struct counters_subgroup *subgroup1 =
+ create_counters_subgroup("subgroup", 0, 5);
+ struct counters_subgroup *subgroup2 =
+ create_counters_subgroup("subgroup", 1, 5);
+
+ CU_ASSERT_FALSE(add_counters_subgroup(NULL, NULL));
+ CU_ASSERT_FALSE(add_counters_subgroup(NULL, subgroup1));
+ CU_ASSERT_FALSE(add_counters_subgroup(group, NULL));
+
+ CU_ASSERT_EQUAL(group->num_subgroups, 0);
+ CU_ASSERT_TRUE(add_counters_subgroup(group, subgroup1));
+ CU_ASSERT_EQUAL(group->num_subgroups, 1);
+ /* Cant add more than num_subgroups to the group */
+ CU_ASSERT_FALSE(add_counters_subgroup(group, subgroup2));
+
+ CU_ASSERT_PTR_NOT_NULL(find_subgroup(group, 0));
+ CU_ASSERT_PTR_NULL(find_subgroup(group, 1));
+
+ delete_counters_group(group);
+ delete_counters_subgroup(subgroup2);
+}
+
+void test_create_subgroup_counter()
+{
+ uint16_t counter_id = 1;
+ char counter_name[] = "my counter";
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 2);
+
+ CU_ASSERT_FALSE(
+ create_subgroup_counter(NULL, counter_id, counter_name));
+ CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id + 1,
+ counter_name));
+ CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id, NULL));
+ CU_ASSERT_EQUAL(subgroup->num_counters, 0);
+ CU_ASSERT_TRUE(
+ create_subgroup_counter(subgroup, counter_id, counter_name));
+ CU_ASSERT_EQUAL(subgroup->num_counters, 1);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_delete_counters_group()
+{
+ struct counters_group *group = create_counters_group("group", 1);
+
+ CU_ASSERT_FALSE(delete_counters_group(NULL));
+ CU_ASSERT_TRUE(delete_counters_group(group));
+}
+
+void test_delete_counters_subgroup()
+{
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 1);
+
+ CU_ASSERT_FALSE(delete_counters_subgroup(NULL));
+ CU_ASSERT_TRUE(delete_counters_subgroup(subgroup));
+}
+
+void test_reset_group_counters()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(reset_group_counters(NULL));
+ CU_ASSERT_TRUE(reset_group_counters(group));
+ CU_ASSERT_EQUAL(counter->counter_value, 0);
+
+ delete_counters_group(group);
+}
+
+void test_reset_subgroup_counters()
+{
+ uint16_t counter_id = 1;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(reset_subgroup_counters(NULL));
+ CU_ASSERT_TRUE(reset_subgroup_counters(subgroup));
+ CU_ASSERT_EQUAL(counter->counter_value, 0);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_increment_counter()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = 100;
+
+ CU_ASSERT_FALSE(increment_counter(NULL, subgroup_id, counter_id));
+ CU_ASSERT_FALSE(increment_counter(group, 100, counter_id));
+ CU_ASSERT_FALSE(increment_counter(group, subgroup_id, 123));
+ CU_ASSERT_TRUE(increment_counter(group, subgroup_id, counter_id));
+ CU_ASSERT_EQUAL(counter->counter_value, 101);
+ CU_ASSERT_EQUAL(subgroup_counters_total(subgroup), 101);
+
+ delete_counters_group(group);
+}
+
+void test_increment_subgroup_counter()
+{
+ int counter_id = 1;
+ uint32_t counter_value = 100;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", 1, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ struct counter *counter = subgroup->counters[counter_id];
+ counter->counter_value = counter_value;
+
+ CU_ASSERT_FALSE(increment_subgroup_counter(NULL, counter_id));
+ CU_ASSERT_FALSE(increment_subgroup_counter(subgroup, counter_id + 1));
+ CU_ASSERT_TRUE(increment_subgroup_counter(subgroup, counter_id));
+ CU_ASSERT_EQUAL(counter->counter_value, counter_value + 1);
+
+ delete_counters_subgroup(subgroup);
+}
+
+void test_dump_counters_group_to_log()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_group *group = create_counters_group("group", 10);
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+ add_counters_subgroup(group, subgroup);
+
+ CU_ASSERT_FALSE(dump_counters_group_to_log(NULL));
+ CU_ASSERT_TRUE(dump_counters_group_to_log(group));
+
+ delete_counters_group(group);
+}
+
+void test_dump_counters_subgroup_to_log()
+{
+ uint16_t subgroup_id = 1;
+ uint16_t counter_id = 1;
+ struct counters_subgroup *subgroup =
+ create_counters_subgroup("subgroup", subgroup_id, 10);
+ create_subgroup_counter(subgroup, counter_id, "counter");
+
+ CU_ASSERT_FALSE(dump_counters_subgroup_to_log(NULL));
+ CU_ASSERT_TRUE(dump_counters_subgroup_to_log(subgroup));
+
+ delete_counters_subgroup(subgroup);
+}
diff --git a/pceplib/test/pcep_utils_counters_test.h b/pceplib/test/pcep_utils_counters_test.h
new file mode 100644
index 0000000000..07236dcb53
--- /dev/null
+++ b/pceplib/test/pcep_utils_counters_test.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_COUNTERS_TEST_H_
+#define PCEP_UTILS_COUNTERS_TEST_H_
+
+void test_create_counters_group(void);
+void test_create_counters_subgroup(void);
+void test_add_counters_subgroup(void);
+void test_create_subgroup_counter(void);
+void test_delete_counters_group(void);
+void test_delete_counters_subgroup(void);
+void test_reset_group_counters(void);
+void test_reset_subgroup_counters(void);
+void test_increment_counter(void);
+void test_increment_subgroup_counter(void);
+void test_dump_counters_group_to_log(void);
+void test_dump_counters_subgroup_to_log(void);
+
+#endif
diff --git a/pceplib/test/pcep_utils_double_linked_list_test.c b/pceplib/test/pcep_utils_double_linked_list_test.c
new file mode 100644
index 0000000000..d2600e66c4
--- /dev/null
+++ b/pceplib/test/pcep_utils_double_linked_list_test.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_double_linked_list_test.h"
+
+typedef struct dll_node_data_ {
+ int int_data;
+
+} dll_node_data;
+
+void test_empty_dl_list()
+{
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NULL(dll_delete_first_node(handle));
+ CU_ASSERT_PTR_NULL(dll_delete_last_node(handle));
+ CU_ASSERT_PTR_NULL(dll_delete_node(handle, NULL));
+
+ dll_destroy(handle);
+}
+
+void test_null_dl_list_handle()
+{
+ dll_destroy(NULL);
+ CU_ASSERT_PTR_NULL(dll_prepend(NULL, NULL));
+ CU_ASSERT_PTR_NULL(dll_append(NULL, NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_first_node(NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_last_node(NULL));
+ CU_ASSERT_PTR_NULL(dll_delete_node(NULL, NULL));
+}
+
+void test_dll_prepend_data()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data3));
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data2));
+ CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data1));
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ double_linked_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+ CU_ASSERT_PTR_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NULL(node->next_node);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_append_data()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ double_linked_list *handle = dll_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data3));
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ double_linked_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+ CU_ASSERT_PTR_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NOT_NULL(node->next_node);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node->prev_node);
+ CU_ASSERT_PTR_NULL(node->next_node);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_first_node()
+{
+ dll_node_data data1, data2;
+ data1.int_data = 1;
+ data2.int_data = 2;
+
+ double_linked_list *handle = dll_initialize();
+
+ /* Test deleting with just 1 node in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /* Test deleting with 2 nodes in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ deleted_data = dll_delete_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data2);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_last_node()
+{
+ dll_node_data data1, data2;
+ data1.int_data = 1;
+ data2.int_data = 2;
+
+ double_linked_list *handle = dll_initialize();
+
+ /* Test deleting with just 1 node in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_last_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /* Test deleting with 2 nodes in the list */
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1));
+ CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2));
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ deleted_data = dll_delete_last_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data1);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+
+ dll_destroy(handle);
+}
+
+
+void test_dll_delete_node()
+{
+ dll_node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+ double_linked_list_node *node1, *node2, *node3;
+ double_linked_list *handle;
+
+ /* Test deleting with just 1 node in the list */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ void *deleted_data = dll_delete_node(handle, node1);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NULL(handle->tail);
+
+ /*
+ * Test deleting the head with 2 nodes in the list
+ */
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ /* Delete the head entry */
+ deleted_data = dll_delete_node(handle, node1);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data1, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data2);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+ dll_destroy(handle);
+
+ /*
+ * Test deleting the tail with 2 nodes in the list
+ */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ /* Delete the tail entry */
+ deleted_data = dll_delete_node(handle, node2);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+ CU_ASSERT_PTR_EQUAL(handle->head->data, &data1);
+ CU_ASSERT_PTR_EQUAL(handle->head, handle->tail);
+ CU_ASSERT_PTR_NULL(handle->head->prev_node);
+ CU_ASSERT_PTR_NULL(handle->head->next_node);
+ dll_destroy(handle);
+
+ /*
+ * Test deleting in the middle with 3 nodes in the list
+ */
+ handle = dll_initialize();
+ node1 = dll_append(handle, &data1);
+ node2 = dll_append(handle, &data2);
+ node3 = dll_append(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node1);
+ CU_ASSERT_PTR_NOT_NULL(node2);
+ CU_ASSERT_PTR_NOT_NULL(node3);
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ /* Delete the middle entry */
+ deleted_data = dll_delete_node(handle, node2);
+ CU_ASSERT_PTR_NOT_NULL(deleted_data);
+ CU_ASSERT_PTR_EQUAL(&data2, deleted_data);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+ CU_ASSERT_PTR_EQUAL(handle->head, node1);
+ CU_ASSERT_PTR_EQUAL(handle->tail, node3);
+ CU_ASSERT_PTR_EQUAL(node1->data, &data1);
+ CU_ASSERT_PTR_EQUAL(node3->data, &data3);
+ CU_ASSERT_PTR_EQUAL(node1->next_node, node3);
+ CU_ASSERT_PTR_EQUAL(node3->prev_node, node1);
+ CU_ASSERT_PTR_NULL(node1->prev_node);
+ CU_ASSERT_PTR_NULL(node3->next_node);
+
+ dll_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_double_linked_list_test.h b/pceplib/test/pcep_utils_double_linked_list_test.h
new file mode 100644
index 0000000000..ddb6467cb6
--- /dev/null
+++ b/pceplib/test/pcep_utils_double_linked_list_test.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_
+#define PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_
+
+void test_empty_dl_list(void);
+void test_null_dl_list_handle(void);
+void test_dll_prepend_data(void);
+void test_dll_append_data(void);
+void test_dll_delete_first_node(void);
+void test_dll_delete_last_node(void);
+void test_dll_delete_node(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_memory_test.c b/pceplib/test/pcep_utils_memory_test.c
new file mode 100644
index 0000000000..b0b528f084
--- /dev/null
+++ b/pceplib/test/pcep_utils_memory_test.c
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_memory.h"
+#include "pcep_utils_memory_test.h"
+
+void *test_pceplib_malloc(void *mem_type, size_t size);
+void *test_pceplib_calloc(void *mem_type, size_t size);
+void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size);
+void *test_pceplib_strdup(void *mem_type, const char *str);
+void test_pceplib_free(void *mem_type, void *ptr);
+void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc,
+ uint32_t alloc_bytes, uint32_t num_free,
+ uint32_t free_bytes);
+void verify_ext_memory_type(void *mt, int num_malloc_calls,
+ int num_calloc_calls, int num_realloc_calls,
+ int num_strdup_calls, int num_free_calls);
+
+struct test_memory_type {
+ int num_malloc_calls;
+ int num_calloc_calls;
+ int num_realloc_calls;
+ int num_strdup_calls;
+ int num_free_calls;
+};
+
+void *test_pceplib_malloc(void *mem_type, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_malloc_calls++;
+ return malloc(size);
+}
+
+void *test_pceplib_calloc(void *mem_type, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_calloc_calls++;
+ return calloc(1, size);
+}
+
+void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size)
+{
+ ((struct test_memory_type *)mem_type)->num_realloc_calls++;
+ return realloc(ptr, size);
+}
+
+void *test_pceplib_strdup(void *mem_type, const char *str)
+{
+ ((struct test_memory_type *)mem_type)->num_strdup_calls++;
+ return strdup(str);
+}
+
+void test_pceplib_free(void *mem_type, void *ptr)
+{
+ ((struct test_memory_type *)mem_type)->num_free_calls++;
+ free(ptr);
+}
+
+void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc,
+ uint32_t alloc_bytes, uint32_t num_free,
+ uint32_t free_bytes)
+{
+ CU_ASSERT_EQUAL(num_alloc, mt->num_allocates);
+ CU_ASSERT_EQUAL(alloc_bytes, mt->total_bytes_allocated);
+ CU_ASSERT_EQUAL(num_free, mt->num_frees);
+ CU_ASSERT_EQUAL(free_bytes, mt->total_bytes_freed);
+}
+
+void verify_ext_memory_type(void *mt, int num_malloc_calls,
+ int num_calloc_calls, int num_realloc_calls,
+ int num_strdup_calls, int num_free_calls)
+{
+ struct test_memory_type *mt_ptr = (struct test_memory_type *)mt;
+ CU_ASSERT_EQUAL(num_malloc_calls, mt_ptr->num_malloc_calls);
+ CU_ASSERT_EQUAL(num_calloc_calls, mt_ptr->num_calloc_calls);
+ CU_ASSERT_EQUAL(num_realloc_calls, mt_ptr->num_realloc_calls);
+ CU_ASSERT_EQUAL(num_strdup_calls, mt_ptr->num_strdup_calls);
+ CU_ASSERT_EQUAL(num_free_calls, mt_ptr->num_free_calls);
+}
+
+void test_memory_internal_impl()
+{
+ int alloc_size = 100;
+ struct pceplib_memory_type *pceplib_infra_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_INFRA;
+ struct pceplib_memory_type *pceplib_messages_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_MESSAGES;
+ int alloc_counter = 1;
+ int free_counter = 1;
+
+ /* reset the memory type counters for easier testing */
+ pceplib_infra_ptr->num_allocates =
+ pceplib_infra_ptr->total_bytes_allocated =
+ pceplib_infra_ptr->num_frees =
+ pceplib_infra_ptr->total_bytes_freed = 0;
+ pceplib_messages_ptr->num_allocates =
+ pceplib_messages_ptr->total_bytes_allocated =
+ pceplib_messages_ptr->num_frees =
+ pceplib_messages_ptr->total_bytes_freed = 0;
+
+ /* Make sure nothing crashes when all these are set NULL, since the
+ * internal default values should still be used. */
+ pceplib_memory_initialize(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ /* Test malloc() */
+ void *ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ verify_memory_type(pceplib_infra_ptr, alloc_counter, alloc_size,
+ free_counter++, 0);
+
+ /* Test calloc() */
+ ptr = pceplib_calloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ alloc_counter++;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ alloc_size * alloc_counter, free_counter++, 0);
+
+ /* Test realloc() */
+ ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ ptr = pceplib_realloc(PCEPLIB_INFRA, ptr, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ alloc_counter += 2;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ alloc_size * alloc_counter, free_counter++, 0);
+
+ /* Test strdup() */
+ ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size);
+ /* Make strdup duplicate (alloc_size - 1) bytes */
+ memset(ptr, 'a', alloc_size);
+ ((char *)ptr)[alloc_size - 1] = '\0';
+ char *str = pceplib_strdup(PCEPLIB_INFRA, (char *)ptr);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_INFRA, ptr);
+ pceplib_free(PCEPLIB_INFRA, str);
+ alloc_counter += 2;
+ free_counter++;
+ verify_memory_type(pceplib_infra_ptr, alloc_counter,
+ (alloc_size * alloc_counter) - 1, free_counter, 0);
+
+ /* Make sure only the pceplib_infra_ptr memory counters are incremented
+ */
+ verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0);
+}
+
+void test_memory_external_impl()
+{
+ int alloc_size = 100;
+ struct pceplib_memory_type *pceplib_infra_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_INFRA;
+ struct pceplib_memory_type *pceplib_messages_ptr =
+ (struct pceplib_memory_type *)PCEPLIB_MESSAGES;
+
+ /* reset the internal memory type counters to later verify they are NOT
+ * incremented since an external impl was provided */
+ pceplib_infra_ptr->num_allocates =
+ pceplib_infra_ptr->total_bytes_allocated =
+ pceplib_infra_ptr->num_frees =
+ pceplib_infra_ptr->total_bytes_freed = 0;
+ pceplib_messages_ptr->num_allocates =
+ pceplib_messages_ptr->total_bytes_allocated =
+ pceplib_messages_ptr->num_frees =
+ pceplib_messages_ptr->total_bytes_freed = 0;
+
+ /* Setup the external memory type */
+ struct test_memory_type infra_mt, messages_mt;
+ void *infra_ptr = &infra_mt;
+ void *messages_ptr = &messages_mt;
+ memset(infra_ptr, 0, sizeof(struct test_memory_type));
+ memset(messages_ptr, 0, sizeof(struct test_memory_type));
+ int free_counter = 1;
+
+ /* Initialize the PCEPlib memory system with an external implementation
+ */
+ pceplib_memory_initialize(infra_ptr, messages_ptr, test_pceplib_malloc,
+ test_pceplib_calloc, test_pceplib_realloc,
+ test_pceplib_strdup, test_pceplib_free);
+
+ /* Test malloc() */
+ void *ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 1, 0, 0, 0, free_counter++);
+
+ /* Test calloc() */
+ ptr = pceplib_calloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 1, 1, 0, 0, free_counter++);
+
+ /* Test realloc() */
+ ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ ptr = pceplib_realloc(PCEPLIB_MESSAGES, ptr, alloc_size);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ verify_ext_memory_type(messages_ptr, 2, 1, 1, 0, free_counter++);
+
+ /* Test strdup() */
+ ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size);
+ /* Make strdup duplicate (alloc_size - 1) bytes */
+ memset(ptr, 'a', alloc_size);
+ ((char *)ptr)[alloc_size - 1] = '\0';
+ char *str = pceplib_strdup(PCEPLIB_MESSAGES, (char *)ptr);
+ CU_ASSERT_PTR_NOT_NULL(ptr);
+ pceplib_free(PCEPLIB_MESSAGES, ptr);
+ pceplib_free(PCEPLIB_MESSAGES, str);
+ verify_ext_memory_type(messages_ptr, 3, 1, 1, 1, free_counter + 1);
+
+ /* Make sure the internal memory counters are NOT incremented */
+ verify_memory_type(pceplib_infra_ptr, 0, 0, 0, 0);
+ verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0);
+
+ verify_ext_memory_type(infra_ptr, 0, 0, 0, 0, 0);
+}
diff --git a/pceplib/test/pcep_utils_memory_test.h b/pceplib/test/pcep_utils_memory_test.h
new file mode 100644
index 0000000000..4e0c3fadf1
--- /dev/null
+++ b/pceplib/test/pcep_utils_memory_test.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_MEMORY_TEST_H_
+#define PCEP_MEMORY_TEST_H_
+
+void test_memory_internal_impl(void);
+void test_memory_external_impl(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_ordered_list_test.c b/pceplib/test/pcep_utils_ordered_list_test.c
new file mode 100644
index 0000000000..fe9ee58825
--- /dev/null
+++ b/pceplib/test/pcep_utils_ordered_list_test.c
@@ -0,0 +1,248 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_ordered_list.h"
+#include "pcep_utils_ordered_list_test.h"
+
+typedef struct node_data_ {
+ int int_data;
+
+} node_data;
+
+
+int node_data_compare(void *list_entry, void *new_entry)
+{
+ /*
+ * < 0 if new_entry < list_entry
+ * == 0 if new_entry == list_entry (new_entry will be inserted after
+ * list_entry) > 0 if new_entry > list_entry
+ */
+
+ return ((node_data *)new_entry)->int_data
+ - ((node_data *)list_entry)->int_data;
+}
+
+
+void test_empty_list()
+{
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ CU_ASSERT_PTR_NOT_NULL(handle);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_PTR_NOT_NULL(handle->compare_function);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_null_list_handle()
+{
+ node_data data;
+ ordered_list_node node_data;
+
+ void *ptr = ordered_list_add_node(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_find(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_first_node(NULL);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_first_node_equals(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = ordered_list_remove_node(NULL, &node_data, &node_data);
+ CU_ASSERT_PTR_NULL(ptr);
+}
+
+
+void test_add_to_list()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data3);
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ ordered_list_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node, NULL);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_find()
+{
+ node_data data1, data2, data3, data_not_inList;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+ data_not_inList.int_data = 5;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data3);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data1);
+
+ ordered_list_node *node = ordered_list_find(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = ordered_list_find(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = ordered_list_find(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node);
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = ordered_list_find(handle, &data_not_inList);
+ CU_ASSERT_PTR_NULL(node);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_first_node()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+ CU_ASSERT_PTR_NULL(handle->head);
+
+ node_data = ordered_list_remove_first_node(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_first_node_equals()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_add_node(handle, &data1);
+ ordered_list_add_node(handle, &data2);
+ ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_first_node_equals(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data3);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ node_data = ordered_list_remove_first_node_equals(handle, &data1);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ ordered_list_destroy(handle);
+}
+
+
+void test_remove_node()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ ordered_list_handle *handle =
+ ordered_list_initialize(node_data_compare);
+
+ ordered_list_node *node1 = ordered_list_add_node(handle, &data1);
+ ordered_list_node *node2 = ordered_list_add_node(handle, &data2);
+ ordered_list_node *node3 = ordered_list_add_node(handle, &data3);
+
+ void *node_data = ordered_list_remove_node(handle, node2, node3);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = ordered_list_remove_node(handle, node1, node2);
+ CU_ASSERT_PTR_NOT_NULL(node_data);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ ordered_list_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_ordered_list_test.h b/pceplib/test/pcep_utils_ordered_list_test.h
new file mode 100644
index 0000000000..3686848b69
--- /dev/null
+++ b/pceplib/test/pcep_utils_ordered_list_test.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_ORDERED_LIST_TEST_H_
+#define PCEP_UTILS_ORDERED_LIST_TEST_H_
+
+void test_empty_list(void);
+void test_null_list_handle(void);
+void test_add_to_list(void);
+void test_find(void);
+void test_remove_first_node(void);
+void test_remove_first_node_equals(void);
+void test_remove_node(void);
+int node_data_compare(void *list_entry, void *new_entry);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_queue_test.c b/pceplib/test/pcep_utils_queue_test.c
new file mode 100644
index 0000000000..1731457789
--- /dev/null
+++ b/pceplib/test/pcep_utils_queue_test.c
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/CUnit.h>
+
+#include "pcep_utils_queue.h"
+#include "pcep_utils_queue_test.h"
+
+typedef struct node_data_ {
+ int int_data;
+
+} node_data;
+
+
+void test_empty_queue()
+{
+ queue_handle *handle = queue_initialize();
+
+ CU_ASSERT_PTR_NOT_NULL(handle);
+ CU_ASSERT_PTR_NULL(handle->head);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ queue_destroy(handle);
+}
+
+
+void test_null_queue_handle()
+{
+ /* test each method handles a NULL handle without crashing */
+ node_data data;
+ queue_destroy(NULL);
+ void *ptr = queue_enqueue(NULL, &data);
+ CU_ASSERT_PTR_NULL(ptr);
+
+ ptr = queue_dequeue(NULL);
+ CU_ASSERT_PTR_NULL(ptr);
+}
+
+
+void test_enqueue()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize();
+
+ queue_enqueue(handle, &data1);
+ queue_enqueue(handle, &data2);
+ queue_enqueue(handle, &data3);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 3);
+
+ queue_node *node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data3);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_NULL(node);
+
+ queue_destroy(handle);
+}
+
+
+void test_enqueue_with_limit()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize_with_size(2);
+
+ queue_node *node = queue_enqueue(handle, &data1);
+ CU_ASSERT_PTR_NOT_NULL(node);
+
+ node = queue_enqueue(handle, &data2);
+ CU_ASSERT_PTR_NOT_NULL(node);
+
+ node = queue_enqueue(handle, &data3);
+ CU_ASSERT_PTR_NULL(node);
+
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node = handle->head;
+ CU_ASSERT_PTR_EQUAL(node->data, &data1);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_EQUAL(node->data, &data2);
+
+ node = node->next_node;
+ CU_ASSERT_PTR_NULL(node);
+
+ queue_destroy(handle);
+}
+
+
+void test_dequeue()
+{
+ node_data data1, data2, data3;
+ data1.int_data = 1;
+ data2.int_data = 2;
+ data3.int_data = 3;
+
+ queue_handle *handle = queue_initialize();
+
+ /* first test dequeue handles an empty queue */
+ void *node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ queue_enqueue(handle, &data1);
+ queue_enqueue(handle, &data2);
+ queue_enqueue(handle, &data3);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data1);
+ CU_ASSERT_EQUAL(handle->num_entries, 2);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data2);
+ CU_ASSERT_EQUAL(handle->num_entries, 1);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_EQUAL(node_data, &data3);
+ CU_ASSERT_EQUAL(handle->num_entries, 0);
+
+ node_data = queue_dequeue(handle);
+ CU_ASSERT_PTR_NULL(node_data);
+
+ queue_destroy(handle);
+}
diff --git a/pceplib/test/pcep_utils_queue_test.h b/pceplib/test/pcep_utils_queue_test.h
new file mode 100644
index 0000000000..16236d0d9d
--- /dev/null
+++ b/pceplib/test/pcep_utils_queue_test.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Javier Garcia <javier.garcia@voltanet.io>
+ *
+ */
+
+/*
+ * Timer definitions to be used internally by the pcep_timers library.
+ */
+
+#ifndef PCEP_UTILS_QUEUE_TEST_H_
+#define PCEP_UTILS_QUEUE_TEST_H_
+
+void test_empty_queue(void);
+void test_null_queue_handle(void);
+void test_enqueue(void);
+void test_enqueue_with_limit(void);
+void test_dequeue(void);
+
+#endif /* PCEPTIMERINTERNALS_H_ */
diff --git a/pceplib/test/pcep_utils_tests.c b/pceplib/test/pcep_utils_tests.c
new file mode 100644
index 0000000000..452b9fa09c
--- /dev/null
+++ b/pceplib/test/pcep_utils_tests.c
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+#include <CUnit/Basic.h>
+#include <CUnit/CUnit.h>
+#include <CUnit/TestDB.h>
+#include "pcep_utils_ordered_list_test.h"
+#include "pcep_utils_queue_test.h"
+#include "pcep_utils_double_linked_list_test.h"
+#include "pcep_utils_counters_test.h"
+#include "pcep_utils_memory_test.h"
+
+
+int main(int argc, char **argv)
+{
+ /* Unused parameters cause compilation warnings */
+ (void)argc;
+ (void)argv;
+
+ CU_initialize_registry();
+
+ CU_pSuite test_queue_suite =
+ CU_add_suite("PCEP Utils Queue Test Suite", NULL, NULL);
+ CU_add_test(test_queue_suite, "test_empty_queue", test_empty_queue);
+ CU_add_test(test_queue_suite, "test_null_queue_handle",
+ test_null_queue_handle);
+ CU_add_test(test_queue_suite, "test_enqueue", test_enqueue);
+ CU_add_test(test_queue_suite, "test_enqueue_with_limit",
+ test_enqueue_with_limit);
+ CU_add_test(test_queue_suite, "test_dequeue", test_dequeue);
+
+ CU_pSuite test_list_suite =
+ CU_add_suite("PCEP Utils Ordered List Test Suite", NULL, NULL);
+ CU_add_test(test_list_suite, "test_empty_list", test_empty_list);
+ CU_add_test(test_list_suite, "test_null_handle", test_null_list_handle);
+ CU_add_test(test_list_suite, "test_add_toList", test_add_to_list);
+ CU_add_test(test_list_suite, "test_find", test_find);
+ CU_add_test(test_list_suite, "test_remove_first_node",
+ test_remove_first_node);
+ CU_add_test(test_list_suite, "test_remove_first_node_equals",
+ test_remove_first_node_equals);
+ CU_add_test(test_list_suite, "test_remove_node", test_remove_node);
+
+ CU_pSuite test_dl_list_suite = CU_add_suite(
+ "PCEP Utils Double Linked List Test Suite", NULL, NULL);
+ CU_add_test(test_dl_list_suite, "test_empty_dl_list",
+ test_empty_dl_list);
+ CU_add_test(test_dl_list_suite, "test_null_dl_handle",
+ test_null_dl_list_handle);
+ CU_add_test(test_dl_list_suite, "test_dll_prepend_data",
+ test_dll_prepend_data);
+ CU_add_test(test_dl_list_suite, "test_dll_append_data",
+ test_dll_append_data);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_first_node",
+ test_dll_delete_first_node);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_last_node",
+ test_dll_delete_last_node);
+ CU_add_test(test_dl_list_suite, "test_dll_delete_node",
+ test_dll_delete_node);
+
+ CU_pSuite test_counters_suite =
+ CU_add_suite("PCEP Utils Counters Test Suite", NULL, NULL);
+ CU_add_test(test_counters_suite, "test_create_counters_group",
+ test_create_counters_group);
+ CU_add_test(test_counters_suite, "test_create_counters_subgroup",
+ test_create_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_add_counters_subgroup",
+ test_add_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_create_subgroup_counter",
+ test_create_subgroup_counter);
+ CU_add_test(test_counters_suite, "test_delete_counters_group",
+ test_delete_counters_group);
+ CU_add_test(test_counters_suite, "test_delete_counters_subgroup",
+ test_delete_counters_subgroup);
+ CU_add_test(test_counters_suite, "test_reset_group_counters",
+ test_reset_group_counters);
+ CU_add_test(test_counters_suite, "test_reset_subgroup_counters",
+ test_reset_subgroup_counters);
+ CU_add_test(test_counters_suite, "test_increment_counter",
+ test_increment_counter);
+ CU_add_test(test_counters_suite, "test_increment_subgroup_counter",
+ test_increment_subgroup_counter);
+ CU_add_test(test_counters_suite, "test_dump_counters_group_to_log",
+ test_dump_counters_group_to_log);
+ CU_add_test(test_counters_suite, "test_dump_counters_subgroup_to_log",
+ test_dump_counters_subgroup_to_log);
+
+ CU_pSuite test_memory_suite =
+ CU_add_suite("PCEP Utils Memory Test Suite", NULL, NULL);
+ CU_add_test(test_memory_suite, "test_memory_internal_impl",
+ test_memory_internal_impl);
+ CU_add_test(test_memory_suite, "test_memory_external_impl",
+ test_memory_external_impl);
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_FailureRecord *failure_record = CU_get_failure_list();
+ if (failure_record != NULL) {
+ printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n");
+ do {
+ printf("\t [%s] [%s] [%s:%d]\n",
+ failure_record->pSuite->pName,
+ failure_record->pTest->pName,
+ failure_record->strFileName,
+ failure_record->uiLineNumber);
+ failure_record = failure_record->pNext;
+
+ } while (failure_record != NULL);
+ }
+
+ CU_pRunSummary run_summary = CU_get_run_summary();
+ int result = run_summary->nTestsFailed;
+ CU_cleanup_registry();
+
+ return result;
+}
diff --git a/pceplib/test/pcep_utils_tests_valgrind.sh b/pceplib/test/pcep_utils_tests_valgrind.sh
new file mode 100755
index 0000000000..6348d82708
--- /dev/null
+++ b/pceplib/test/pcep_utils_tests_valgrind.sh
@@ -0,0 +1,2 @@
+source pceplib/test/pcep_tests_valgrind.sh
+valgrind_test pceplib/test/pcep_utils_tests
diff --git a/pceplib/test/subdir.am b/pceplib/test/subdir.am
new file mode 100644
index 0000000000..0ae61d1bce
--- /dev/null
+++ b/pceplib/test/subdir.am
@@ -0,0 +1,122 @@
+if PATHD_PCEP
+if PATHD_PCEP_TEST
+
+# The default Automake target is check, add a test target to call check.
+# Also make sure the binaries are current before running the tests.
+test: pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests
+
+check_SCRIPTS = pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests
+TESTS = $(check_SCRIPTS)
+
+
+# Definitions to build the Unit Test binaries with CUnit
+noinst_PROGRAMS += pceplib/test/pcep_msg_tests \
+ pceplib/test/pcep_pcc_api_tests \
+ pceplib/test/pcep_session_logic_tests \
+ pceplib/test/pcep_socket_comm_tests \
+ pceplib/test/pcep_timers_tests \
+ pceplib/test/pcep_utils_tests
+
+noinst_HEADERS += pceplib/test/pcep_msg_messages_test.h \
+ pceplib/test/pcep_msg_object_error_types_test.h \
+ pceplib/test/pcep_msg_objects_test.h \
+ pceplib/test/pcep_msg_tlvs_test.h \
+ pceplib/test/pcep_msg_tools_test.h \
+ pceplib/test/pcep_pcc_api_test.h \
+ pceplib/test/pcep_session_logic_loop_test.h \
+ pceplib/test/pcep_session_logic_states_test.h \
+ pceplib/test/pcep_session_logic_test.h \
+ pceplib/test/pcep_socket_comm_loop_test.h \
+ pceplib/test/pcep_socket_comm_test.h \
+ pceplib/test/pcep_timers_event_loop_test.h \
+ pceplib/test/pcep_timers_test.h \
+ pceplib/test/pcep_utils_counters_test.h \
+ pceplib/test/pcep_utils_double_linked_list_test.h \
+ pceplib/test/pcep_utils_memory_test.h \
+ pceplib/test/pcep_utils_ordered_list_test.h \
+ pceplib/test/pcep_utils_queue_test.h
+
+pceplib_test_pcep_msg_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_msg_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_msg_tests_SOURCES = pceplib/test/pcep_msg_messages_test.c \
+ pceplib/test/pcep_msg_messages_tests.c \
+ pceplib/test/pcep_msg_object_error_types_test.c \
+ pceplib/test/pcep_msg_objects_test.c \
+ pceplib/test/pcep_msg_tlvs_test.c \
+ pceplib/test/pcep_msg_tools_test.c
+
+# The pcc_api_tests and pcep_session_logic_tests use the
+# socket_comm_mock, so the LDADD variable needs to be modified
+pceplib_test_pcep_pcc_api_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_pcc_api_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_pcc_api_tests_SOURCES = pceplib/test/pcep_pcc_api_test.c pceplib/test/pcep_pcc_api_tests.c
+
+pceplib_test_pcep_session_logic_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_session_logic_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_session_logic_tests_SOURCES = pceplib/test/pcep_session_logic_loop_test.c \
+ pceplib/test/pcep_session_logic_states_test.c \
+ pceplib/test/pcep_session_logic_test.c \
+ pceplib/test/pcep_session_logic_tests.c
+
+pceplib_test_pcep_socket_comm_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_socket_comm_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_socket_comm_tests_SOURCES = pceplib/test/pcep_socket_comm_loop_test.c \
+ pceplib/test/pcep_socket_comm_test.c \
+ pceplib/test/pcep_socket_comm_tests.c
+
+pceplib_test_pcep_timers_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_timers_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_timers_tests_SOURCES = pceplib/test/pcep_timers_event_loop_test.c \
+ pceplib/test/pcep_timers_test.c \
+ pceplib/test/pcep_timers_tests.c
+
+pceplib_test_pcep_utils_tests_CFLAGS = -I$(top_srcdir)/pceplib
+pceplib_test_pcep_utils_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread
+pceplib_test_pcep_utils_tests_SOURCES = pceplib/test/pcep_utils_counters_test.c \
+ pceplib/test/pcep_utils_double_linked_list_test.c \
+ pceplib/test/pcep_utils_memory_test.c \
+ pceplib/test/pcep_utils_ordered_list_test.c \
+ pceplib/test/pcep_utils_queue_test.c \
+ pceplib/test/pcep_utils_tests.c
+
+# These test scripts will call the test binaries
+# defined above in noinst_PROGRAMS with Valgrind
+if HAVE_VALGRIND_PCEP
+
+dist_noinst_SCRIPTS = pceplib/test/pcep_pcc_api_tests_valgrind.sh \
+ pceplib/test/pcep_session_logic_tests_valgrind.sh \
+ pceplib/test/pcep_socket_comm_tests_valgrind.sh \
+ pceplib/test/pcep_timers_tests_valgrind.sh \
+ pceplib/test/pcep_utils_tests_valgrind.sh \
+ pceplib/test/pcep_msg_tests_valgrind.sh \
+ pceplib/test/pcep_tests_valgrind.sh
+
+check_SCRIPTS += pceplib/test/pcep_msg_tests_valgrind.sh \
+ pceplib/test/pcep_pcc_api_tests_valgrind.sh \
+ pceplib/test/pcep_session_logic_tests_valgrind.sh \
+ pceplib/test/pcep_socket_comm_tests_valgrind.sh \
+ pceplib/test/pcep_timers_tests_valgrind.sh \
+ pceplib/test/pcep_utils_tests_valgrind.sh
+
+TESTS += $(check_SCRIPTS)
+
+
+
+pceplib/test/pcep_msg_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_msg_tests_valgrind.sh
+pceplib/test/pcep_pcc_api_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_pcc_api_tests_valgrind.sh
+pceplib/test/pcep_session_logic_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_session_logic_tests_valgrind.sh
+pceplib/test/pcep_socket_comm_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_socket_comm_tests_valgrind.sh
+pceplib/test/pcep_timers_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_timers_tests_valgrind.sh
+pceplib/test/pcep_utils_tests_valgrind.sh:
+ chmod +x pceplib/test/pcep_utils_tests_valgrind.sh
+
+
+endif
+
+endif
+endif
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
index 941d067619..d36a275f85 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -66,7 +66,6 @@ static bool mtrace_fwd_info_weak(struct pim_instance *pim,
struct pim_nexthop nexthop;
struct interface *ifp_in;
struct in_addr nh_addr;
- char nexthop_str[INET_ADDRSTRLEN];
nh_addr.s_addr = INADDR_ANY;
@@ -82,10 +81,8 @@ static bool mtrace_fwd_info_weak(struct pim_instance *pim,
zlog_debug("mtrace pim_nexthop_lookup OK");
if (PIM_DEBUG_MTRACE)
- zlog_debug("mtrace next_hop=%s",
- inet_ntop(nexthop.mrib_nexthop_addr.family,
- &nexthop.mrib_nexthop_addr.u.prefix,
- nexthop_str, sizeof(nexthop_str)));
+ zlog_debug("mtrace next_hop=%pI4",
+ &nexthop.mrib_nexthop_addr.u.prefix4);
if (nexthop.mrib_nexthop_addr.family == AF_INET)
nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
@@ -114,7 +111,6 @@ static bool mtrace_fwd_info(struct pim_instance *pim,
struct interface *ifp_in;
struct in_addr nh_addr;
uint32_t total;
- char up_str[INET_ADDRSTRLEN];
memset(&sg, 0, sizeof(struct prefix_sg));
sg.src = mtracep->src_addr;
@@ -142,9 +138,7 @@ static bool mtrace_fwd_info(struct pim_instance *pim,
total = htonl(MTRACE_UNKNOWN_COUNT);
if (PIM_DEBUG_MTRACE)
- zlog_debug("fwd_info: upstream next hop=%s",
- inet_ntop(AF_INET, &(nh_addr), up_str,
- sizeof(up_str)));
+ zlog_debug("fwd_info: upstream next hop=%pI4", &nh_addr);
if (up->channel_oil)
total = up->channel_oil->cc.pktcnt;
@@ -198,31 +192,19 @@ static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
struct igmp_mtrace_rsp *mrspp)
{
- char inc_str[INET_ADDRSTRLEN];
- char out_str[INET_ADDRSTRLEN];
- char prv_str[INET_ADDRSTRLEN];
+ struct in_addr incoming = mrspp->incoming;
+ struct in_addr outgoing = mrspp->outgoing;
+ struct in_addr prev_hop = mrspp->prev_hop;
zlog_debug(
- "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
- rsp, ntohl(qry_id), mrspp->arrival,
- inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
- sizeof(inc_str)),
- inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
- sizeof(out_str)),
- inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
- sizeof(prv_str)),
- mrspp->rtg_proto, mrspp->fwd_code);
+ "Rx mt(%d) qid=%ud arr=%x in=%pI4 out=%pI4 prev=%pI4 proto=%d fwd=%d",
+ rsp, ntohl(qry_id), mrspp->arrival, &incoming, &outgoing,
+ &prev_hop, mrspp->rtg_proto, mrspp->fwd_code);
}
static void mtrace_debug(struct pim_interface *pim_ifp,
struct igmp_mtrace *mtracep, int mtrace_len)
{
- char inc_str[INET_ADDRSTRLEN];
- char grp_str[INET_ADDRSTRLEN];
- char src_str[INET_ADDRSTRLEN];
- char dst_str[INET_ADDRSTRLEN];
- char rsp_str[INET_ADDRSTRLEN];
-
struct in_addr ga, sa, da, ra;
ga = mtracep->grp_addr;
@@ -231,19 +213,10 @@ static void mtrace_debug(struct pim_interface *pim_ifp,
ra = mtracep->rsp_addr;
zlog_debug(
- "Rx mtrace packet incoming on %s: hops=%d type=%d size=%d, grp=%s, src=%s, dst=%s rsp=%s ttl=%d qid=%ud",
- inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
- sizeof(inc_str)),
- mtracep->hops, mtracep->type, mtrace_len,
- inet_ntop(AF_INET, &ga, grp_str,
- sizeof(grp_str)),
- inet_ntop(AF_INET, &sa, src_str,
- sizeof(src_str)),
- inet_ntop(AF_INET, &da, dst_str,
- sizeof(dst_str)),
- inet_ntop(AF_INET, &ra, rsp_str,
- sizeof(rsp_str)),
- mtracep->rsp_ttl, ntohl(mtracep->qry_id));
+ "Rx mtrace packet incoming on %pI4: hops=%d type=%d size=%d, grp=%pI4, src=%pI4, dst=%pI4 rsp=%pI4 ttl=%d qid=%ud",
+ &pim_ifp->primary_address, mtracep->hops, mtracep->type,
+ mtrace_len, &ga, &sa, &da, &ra, mtracep->rsp_ttl,
+ ntohl(mtracep->qry_id));
if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
int i;
@@ -290,8 +263,6 @@ static int mtrace_send_packet(struct interface *ifp,
ssize_t sent;
int ret;
int fd;
- char if_str[INET_ADDRSTRLEN];
- char rsp_str[INET_ADDRSTRLEN];
uint8_t ttl;
memset(&to, 0, sizeof(to));
@@ -301,13 +272,11 @@ static int mtrace_send_packet(struct interface *ifp,
if (PIM_DEBUG_MTRACE) {
struct in_addr if_addr;
+ struct in_addr rsp_addr = mtracep->rsp_addr;
if_addr = mtrace_primary_address(ifp);
- zlog_debug(
- "Sending mtrace packet to %s on %s",
- inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
- sizeof(rsp_str)),
- inet_ntop(AF_INET, &if_addr, if_str, sizeof(if_str)));
+ zlog_debug("Sending mtrace packet to %pI4 on %pI4", &rsp_addr,
+ &if_addr);
}
fd = pim_socket_raw(IPPROTO_IGMP);
@@ -514,7 +483,6 @@ static int mtrace_send_mc_response(struct pim_instance *pim,
struct listnode *chnextnode;
struct pim_ifchannel *ch = NULL;
int ret = -1;
- char buf[PREFIX_STRLEN];
memset(&sg, 0, sizeof(struct prefix_sg));
sg.grp = mtracep->rsp_addr;
@@ -523,11 +491,11 @@ static int mtrace_send_mc_response(struct pim_instance *pim,
if (c_oil == NULL) {
if (PIM_DEBUG_MTRACE) {
+ struct in_addr rsp_addr = mtracep->rsp_addr;
+
zlog_debug(
- "Dropping mtrace multicast response packet len=%u to %s",
- (unsigned int)mtrace_len,
- inet_ntop(AF_INET, &mtracep->rsp_addr,
- buf, sizeof(buf)));
+ "Dropping mtrace multicast response packet len=%u to %pI4",
+ (unsigned int)mtrace_len, &rsp_addr);
}
return -1;
}
@@ -562,7 +530,6 @@ static int mtrace_send_response(struct pim_instance *pim,
if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
struct pim_rpf *p_rpf;
- char grp_str[INET_ADDRSTRLEN];
if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
return mtrace_send_mc_response(pim, mtracep,
@@ -571,11 +538,11 @@ static int mtrace_send_response(struct pim_instance *pim,
p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
if (p_rpf == NULL) {
- if (PIM_DEBUG_MTRACE)
- zlog_debug("mtrace no RP for %s",
- inet_ntop(AF_INET,
- &(mtracep->rsp_addr),
- grp_str, sizeof(grp_str)));
+ if (PIM_DEBUG_MTRACE) {
+ struct in_addr rsp_addr = mtracep->rsp_addr;
+
+ zlog_debug("mtrace no RP for %pI4", &rsp_addr);
+ }
return -1;
}
nexthop = p_rpf->source_nexthop;
diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c
index ddd8dc6bf9..b613937f59 100644
--- a/pimd/pim_msdp_socket.c
+++ b/pimd/pim_msdp_socket.c
@@ -67,7 +67,6 @@ static int pim_msdp_sock_accept(struct thread *thread)
int accept_sock;
int msdp_sock;
struct pim_msdp_peer *mp;
- char buf[SU_ADDRSTRLEN];
sockunion_init(&su);
@@ -96,8 +95,7 @@ static int pim_msdp_sock_accept(struct thread *thread)
++pim->msdp.rejected_accepts;
if (PIM_DEBUG_MSDP_EVENTS) {
flog_err(EC_PIM_MSDP_PACKET,
- "msdp peer connection refused from %s",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ "msdp peer connection refused from %pSU", &su);
}
close(msdp_sock);
return -1;
@@ -113,8 +111,8 @@ static int pim_msdp_sock_accept(struct thread *thread)
if (mp->fd >= 0) {
if (PIM_DEBUG_MSDP_EVENTS) {
zlog_notice(
- "msdp peer new connection from %s stop old connection",
- sockunion2str(&su, buf, SU_ADDRSTRLEN));
+ "msdp peer new connection from %pSU stop old connection",
+ &su);
}
pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
}
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 02c272f47c..b6d7ab2416 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -698,6 +698,7 @@ fi
%endif
%if %{with_pathd}
%{_sbindir}/pathd
+ %{_libdir}/frr/modules/pathd_pcep.so
%endif
%{_libdir}/libfrr.so*
%{_libdir}/libfrrcares*
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index fed732b843..0095aed547 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -638,7 +638,6 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import,
static int sharp_debug_nexthops(struct zapi_route *api)
{
int i;
- char buf[PREFIX_STRLEN];
if (api->nexthop_num == 0) {
zlog_debug(
@@ -653,20 +652,16 @@ static int sharp_debug_nexthops(struct zapi_route *api)
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV4:
zlog_debug(
- " Nexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d",
- inet_ntop(AF_INET, &znh->gate.ipv4.s_addr, buf,
- sizeof(buf)),
- znh->type, znh->ifindex, znh->vrf_id,
- znh->label_num);
+ " Nexthop %pI4, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv4.s_addr, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
case NEXTHOP_TYPE_IPV6:
zlog_debug(
- " Nexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d",
- inet_ntop(AF_INET6, &znh->gate.ipv6, buf,
- sizeof(buf)),
- znh->type, znh->ifindex, znh->vrf_id,
- znh->label_num);
+ " Nexthop %pI6, type: %d, ifindex: %d, vrf: %d, label_num: %d",
+ &znh->gate.ipv6, znh->type, znh->ifindex,
+ znh->vrf_id, znh->label_num);
break;
case NEXTHOP_TYPE_IFINDEX:
zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d",
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json
index d1927ae49a..b436d5562e 100644
--- a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json
@@ -8,9 +8,10 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json
index 25b47f18ec..c7c7b96ee7 100644
--- a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json
@@ -6,9 +6,10 @@
"status":"up",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json
index 5193f2a6e2..fc9e145340 100644
--- a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json
@@ -8,9 +8,10 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json
index 9e4bd2633f..620c6ddcd4 100644
--- a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json
+++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json
@@ -6,9 +6,10 @@
"status":"down",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf
index 4d636ab052..688f2e839c 100644
--- a/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf
@@ -6,6 +6,7 @@ bfd
profile slowtx
receive-interval 800
transmit-interval 800
+ echo receive-interval 400
!
peer 172.16.0.1 interface r1-eth0
profile slowtx
diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json
index 3df9ec9c9d..503f776aec 100644
--- a/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json
@@ -12,6 +12,7 @@
"remote-id": "*",
"remote-receive-interval": 800,
"remote-transmit-interval": 800,
+ "remote-echo-receive-interval": 400,
"status": "up",
"transmit-interval": 800,
"uptime": "*",
@@ -27,7 +28,7 @@
"receive-interval": 250,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 300,
"remote-transmit-interval": 300,
diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf
index 23a39a6ee0..700c46ba1e 100644
--- a/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf
@@ -10,6 +10,7 @@ bfd
profile fasttx
receive-interval 250
transmit-interval 250
+ echo receive-interval disabled
!
peer 172.16.0.2 interface r2-eth0
profile slowtx
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
index d2d0c601c3..d987a0ae7d 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
@@ -12,6 +12,7 @@
"remote-id": "*",
"remote-receive-interval": 250,
"remote-transmit-interval": 250,
+ "remote-echo-receive-interval": 0,
"status": "up",
"transmit-interval": 300,
"uptime": "*",
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
index 2c2e136abf..c73296ac97 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
@@ -29,7 +29,7 @@
"receive-interval": 300,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 300,
"remote-transmit-interval": 300,
diff --git a/tests/topotests/bfd-topo1/r2/peers.json b/tests/topotests/bfd-topo1/r2/peers.json
index 5035d643c5..267459c7a8 100644
--- a/tests/topotests/bfd-topo1/r2/peers.json
+++ b/tests/topotests/bfd-topo1/r2/peers.json
@@ -4,7 +4,7 @@
"status": "up"
},
{
- "remote-echo-interval": 100,
+ "remote-echo-receive-interval": 100,
"peer": "192.168.1.1",
"status": "up"
},
diff --git a/tests/topotests/bfd-topo2/r1/peers.json b/tests/topotests/bfd-topo2/r1/peers.json
index b14351cd81..9bce991d0d 100644
--- a/tests/topotests/bfd-topo2/r1/peers.json
+++ b/tests/topotests/bfd-topo2/r1/peers.json
@@ -8,10 +8,11 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
},
{
"multihop":false,
@@ -21,9 +22,10 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-topo2/r2/peers.json b/tests/topotests/bfd-topo2/r2/peers.json
index 29075fcc80..ec2135ce37 100644
--- a/tests/topotests/bfd-topo2/r2/peers.json
+++ b/tests/topotests/bfd-topo2/r2/peers.json
@@ -3,39 +3,42 @@
"status": "up",
"transmit-interval": 300,
"remote-receive-interval": 300,
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"diagnostic": "ok",
"multihop": false,
"interface": "r2-eth0",
"remote-transmit-interval": 300,
"receive-interval": 300,
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-diagnostic": "ok"
},
{
"status": "up",
"transmit-interval": 300,
"remote-receive-interval": 300,
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"diagnostic": "ok",
"multihop": false,
"interface": "r2-eth2",
"remote-transmit-interval": 300,
"receive-interval": 300,
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-diagnostic": "ok"
},
{
"status": "up",
"transmit-interval": 300,
"remote-receive-interval": 300,
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"diagnostic": "ok",
"multihop": false,
"interface": "r2-eth1",
"remote-transmit-interval": 300,
"receive-interval": 300,
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-diagnostic": "ok",
"peer": "10.0.3.1"
}
diff --git a/tests/topotests/bfd-topo2/r3/peers.json b/tests/topotests/bfd-topo2/r3/peers.json
index 6698bff201..c19c980338 100644
--- a/tests/topotests/bfd-topo2/r3/peers.json
+++ b/tests/topotests/bfd-topo2/r3/peers.json
@@ -3,13 +3,14 @@
"status": "up",
"transmit-interval": 300,
"remote-receive-interval": 300,
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"diagnostic": "ok",
"multihop": false,
"interface": "r3-eth0",
"remote-transmit-interval": 300,
"receive-interval": 300,
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-diagnostic": "ok",
"peer": "10.0.3.2"
}
diff --git a/tests/topotests/bfd-topo2/r4/peers.json b/tests/topotests/bfd-topo2/r4/peers.json
index 83101eb47f..dd26b9b580 100644
--- a/tests/topotests/bfd-topo2/r4/peers.json
+++ b/tests/topotests/bfd-topo2/r4/peers.json
@@ -8,10 +8,11 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
},
{
"multihop":false,
@@ -21,9 +22,10 @@
"remote-diagnostic":"ok",
"receive-interval":300,
"transmit-interval":300,
- "echo-interval":0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval":0,
"remote-receive-interval":300,
"remote-transmit-interval":300,
- "remote-echo-interval":50
+ "remote-echo-receive-interval":50
}
]
diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json
index 56205d538b..f8a354fc20 100644
--- a/tests/topotests/bfd-topo3/r1/bfd-peers.json
+++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json
@@ -2,7 +2,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"local": "2001:db8:1::1",
"minimum-ttl": 253,
@@ -12,7 +13,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
@@ -24,7 +25,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r1-eth0",
"local": "2001:db8:1::1",
@@ -34,7 +36,7 @@
"receive-interval": 600,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 600,
"remote-transmit-interval": 600,
@@ -46,7 +48,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"local": "192.168.1.1",
"minimum-ttl": 254,
@@ -56,7 +59,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json
index cb8985b13e..786d66dbe3 100644
--- a/tests/topotests/bfd-topo3/r2/bfd-peers.json
+++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json
@@ -2,7 +2,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r2-eth0",
"local": "2001:db8:1::2",
@@ -12,7 +13,7 @@
"receive-interval": 600,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 600,
"remote-transmit-interval": 600,
@@ -24,7 +25,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r2-eth1",
"local": "2001:db8:2::2",
@@ -34,7 +36,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json
index 8be35fd084..1f58663a4e 100644
--- a/tests/topotests/bfd-topo3/r3/bfd-peers.json
+++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json
@@ -2,7 +2,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r3-eth1",
"local": "2001:db8:3::2",
@@ -12,7 +13,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
@@ -24,7 +25,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r3-eth0",
"local": "2001:db8:2::1",
@@ -34,7 +36,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
@@ -46,7 +48,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"local": "192.168.2.1",
"minimum-ttl": 254,
@@ -56,7 +59,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json
index e2e6722ef4..5477f39120 100644
--- a/tests/topotests/bfd-topo3/r4/bfd-peers.json
+++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json
@@ -2,7 +2,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"local": "2001:db8:3::1",
"minimum-ttl": 253,
@@ -12,7 +13,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
@@ -24,7 +25,8 @@
{
"detect-multiplier": 3,
"diagnostic": "ok",
- "echo-interval": 0,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
"id": "*",
"interface": "r4-eth0",
"local": "2001:db8:3::1",
@@ -34,7 +36,7 @@
"receive-interval": 2000,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
- "remote-echo-interval": 50,
+ "remote-echo-receive-interval": 50,
"remote-id": "*",
"remote-receive-interval": 2000,
"remote-transmit-interval": 2000,
diff --git a/tests/topotests/bfd-vrf-topo1/r2/peers.json b/tests/topotests/bfd-vrf-topo1/r2/peers.json
index 5035d643c5..267459c7a8 100644
--- a/tests/topotests/bfd-vrf-topo1/r2/peers.json
+++ b/tests/topotests/bfd-vrf-topo1/r2/peers.json
@@ -4,7 +4,7 @@
"status": "up"
},
{
- "remote-echo-interval": 100,
+ "remote-echo-receive-interval": 100,
"peer": "192.168.1.1",
"status": "up"
},
diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py
index 89f5554d41..db4eab9d3d 100755
--- a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py
+++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py
@@ -505,10 +505,8 @@ def test_r1_mplsvpn_VrfTable():
associated_int = r1_snmp.get(
"mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a"))
)
- assertmsg = (
- "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format(
- associated_int
- )
+ assertmsg = "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format(
+ associated_int
)
assert associated_int == "3", assertmsg
@@ -634,7 +632,6 @@ rte_table_test = {
"C0 A8 C8 0A",
'""',
],
- "mplsL3VpnVrfRteInetCidrIfIndex": ["5", "6", "4", "5", "0", "6", "0"],
"mplsL3VpnVrfRteInetCidrType": [
"local(3)",
"local(3)",
@@ -732,8 +729,24 @@ def test_r1_mplsvpn_rte_table():
)
if passed:
break
+ # generate ifindex row grabbing ifindices from vtysh
+ if passed:
+ ifindex_row = [
+ router_interface_get_ifindex(r1r, "eth3"),
+ router_interface_get_ifindex(r1r, "eth4"),
+ router_interface_get_ifindex(r1r, "eth2"),
+ router_interface_get_ifindex(r1r, "eth3"),
+ "0",
+ router_interface_get_ifindex(r1r, "eth4"),
+ "0",
+ ]
+ if not r1_snmp.test_oid_walk(
+ "mplsL3VpnVrfRteInetCidrIfIndex", ifindex_row, oid_list
+ ):
+ passed = False
+
print("passed {}".format(passed))
- # assert passed, assertmsg
+ assert passed, assertmsg
def test_memory_leak():
diff --git a/tests/topotests/isis-snmp/ce3/zebra.conf b/tests/topotests/isis-snmp/ce3/zebra.conf
new file mode 100644
index 0000000000..c6a5824d15
--- /dev/null
+++ b/tests/topotests/isis-snmp/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r1/isisd.conf b/tests/topotests/isis-snmp/r1/isisd.conf
new file mode 100644
index 0000000000..dd32d3b8a5
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/isisd.conf
@@ -0,0 +1,24 @@
+hostname r1
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r1-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r1-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis-snmp/r1/ldpd.conf b/tests/topotests/isis-snmp/r1/ldpd.conf
new file mode 100644
index 0000000000..4ec296ca5a
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/ldpd.conf
@@ -0,0 +1,26 @@
+hostname r1
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+agentx
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth0
+ !
+ interface r1-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r1/show_ip_route.ref b/tests/topotests/isis-snmp/r1/show_ip_route.ref
new file mode 100644
index 0000000000..dc8f19dad0
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-snmp/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-snmp/r1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..390fda749e
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r1-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r1-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-snmp/r1/snmpd.conf b/tests/topotests/isis-snmp/r1/snmpd.conf
new file mode 100644
index 0000000000..b37911da36
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/isis-snmp/r1/zebra.conf b/tests/topotests/isis-snmp/r1/zebra.conf
new file mode 100644
index 0000000000..6ac341e431
--- /dev/null
+++ b/tests/topotests/isis-snmp/r1/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r1
+!
+debug zebra kernel
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra nht
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to rt4
+ ip address 14.0.0.1/24
+!
+interface r1-eth1
+ description to rt3
+ ip address 13.0.0.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r2/isisd.conf b/tests/topotests/isis-snmp/r2/isisd.conf
new file mode 100644
index 0000000000..4403d8913b
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/isisd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r2-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r2-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis-snmp/r2/ldpd.conf b/tests/topotests/isis-snmp/r2/ldpd.conf
new file mode 100644
index 0000000000..eb963fe41c
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r2/show_ip_route.ref b/tests/topotests/isis-snmp/r2/show_ip_route.ref
new file mode 100644
index 0000000000..2bcee96064
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-snmp/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-snmp/r2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..52550daf14
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r2-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r2-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-snmp/r2/snmpd.conf b/tests/topotests/isis-snmp/r2/snmpd.conf
new file mode 100644
index 0000000000..0f779b8b91
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:2.2.2.2:161
+
+com2sec public 2.2.2.2 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/isis-snmp/r2/zebra.conf b/tests/topotests/isis-snmp/r2/zebra.conf
new file mode 100644
index 0000000000..4aa7440c33
--- /dev/null
+++ b/tests/topotests/isis-snmp/r2/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r2
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to rt5
+ ip address 25.0.0.2/24
+!
+interface r2-eth1
+ description to rt3
+ ip address 23.0.0.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r3/isisd.conf b/tests/topotests/isis-snmp/r3/isisd.conf
new file mode 100644
index 0000000000..e06fe8c1f9
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/isisd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r3-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis-snmp/r3/ldpd.conf b/tests/topotests/isis-snmp/r3/ldpd.conf
new file mode 100644
index 0000000000..2935caf13b
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r3/show_ip_route.ref b/tests/topotests/isis-snmp/r3/show_ip_route.ref
new file mode 100644
index 0000000000..da46f1dfe2
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-snmp/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-snmp/r3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..3aafab4e2e
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r3-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r3-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-snmp/r3/snmpd.conf b/tests/topotests/isis-snmp/r3/snmpd.conf
new file mode 100644
index 0000000000..3f3501a6fd
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:3.3.3.3:161
+
+com2sec public 3.3.3.3 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/isis-snmp/r3/zebra.conf b/tests/topotests/isis-snmp/r3/zebra.conf
new file mode 100644
index 0000000000..6b76114d4d
--- /dev/null
+++ b/tests/topotests/isis-snmp/r3/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r3
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to ce3
+ ip address 172.16.1.3/24
+!
+interface r3-eth1
+ description to rt2
+ ip address 13.0.0.3/24
+!
+interface r3-eth2
+ description to rt1
+ ip address 23.0.0.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r4/isisd.conf b/tests/topotests/isis-snmp/r4/isisd.conf
new file mode 100644
index 0000000000..1256141da9
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/isisd.conf
@@ -0,0 +1,24 @@
+hostname r4
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r4-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r4-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis-snmp/r4/ldpd.conf b/tests/topotests/isis-snmp/r4/ldpd.conf
new file mode 100644
index 0000000000..b27952514b
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r4
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 4.4.4.4
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r4-eth0
+ !
+ interface r4-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r4/show_ip_route.ref b/tests/topotests/isis-snmp/r4/show_ip_route.ref
new file mode 100644
index 0000000000..da46f1dfe2
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-snmp/r4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-snmp/r4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..86fcfea1a6
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r4-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r4-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-snmp/r4/snmpd.conf b/tests/topotests/isis-snmp/r4/snmpd.conf
new file mode 100644
index 0000000000..e5e336d888
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:4.4.4.4:161
+
+com2sec public 4.4.4.4 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/isis-snmp/r4/zebra.conf b/tests/topotests/isis-snmp/r4/zebra.conf
new file mode 100644
index 0000000000..fa13601164
--- /dev/null
+++ b/tests/topotests/isis-snmp/r4/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r4
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to rt1
+ ip address 14.0.0.4/24
+!
+interface r4-eth1
+ description to rt5
+ ip address 45.0.0.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r5/isisd.conf b/tests/topotests/isis-snmp/r5/isisd.conf
new file mode 100644
index 0000000000..58859041a9
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/isisd.conf
@@ -0,0 +1,25 @@
+hostname r5
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r5-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r5-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis-snmp/r5/ldpd.conf b/tests/topotests/isis-snmp/r5/ldpd.conf
new file mode 100644
index 0000000000..f3ba867a9f
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r5
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 5.5.5.5
+ !
+ address-family ipv4
+ discovery transport-address 5.5.5.5
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r5-eth0
+ !
+ interface r5-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r5/ldpdconf b/tests/topotests/isis-snmp/r5/ldpdconf
new file mode 100644
index 0000000000..fc700608b5
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/ldpdconf
@@ -0,0 +1,25 @@
+hostname r5
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 5.5.5.5
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r5-eth0
+ !
+ interface r5-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/r5/show_ip_route.ref b/tests/topotests/isis-snmp/r5/show_ip_route.ref
new file mode 100644
index 0000000000..da46f1dfe2
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis-snmp/r5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-snmp/r5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..994e8166ce
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r5-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r5-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis-snmp/r5/snmpd.conf b/tests/topotests/isis-snmp/r5/snmpd.conf
new file mode 100644
index 0000000000..5bebbdebd4
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:5.5.5.5:161
+
+com2sec public 5.5.5.5 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/isis-snmp/r5/zebra.conf b/tests/topotests/isis-snmp/r5/zebra.conf
new file mode 100644
index 0000000000..7230129f22
--- /dev/null
+++ b/tests/topotests/isis-snmp/r5/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r5
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface r5-eth0
+ description to rt2
+ ip address 25.0.0.5/24
+!
+interface r5-eth1
+ description to rt4
+ ip address 45.0.0.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis-snmp/test_isis_snmp.dot b/tests/topotests/isis-snmp/test_isis_snmp.dot
new file mode 100644
index 0000000000..6d8c893712
--- /dev/null
+++ b/tests/topotests/isis-snmp/test_isis_snmp.dot
@@ -0,0 +1,114 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="Test Topology - LDP-VPLS 1";
+
+ # Routers
+ ce3 [
+ shape=doubleoctagon
+ label="ce3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n14.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s2\n25.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="s3\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="s4\n45.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ shape=oval,
+ label="s5\n13.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ shape=oval,
+ label="s6\n23.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ ce3 -- s3 [label="eth0\n.3"];
+
+ r1 -- s1 [label="eth1\n.1"];
+ r1 -- s5 [label="eth2\n.1"];
+
+ r2 -- s2 [label="eth1\n.2"];
+ r2 -- s6 [label="eth2\n.2"];
+
+ r3 -- s5 [label="eth1\n.3"];
+ r3 -- s6 [label="eth2\n.3"];
+
+ r4 -- s1 [label="eth1\n.4"];
+ r4 -- s4 [label="eth2\n.4"];
+
+ r5 -- s2 [label="eth1\n.5"];
+ r5 -- s4 [label="eth2\n.5"];
+}
diff --git a/tests/topotests/isis-snmp/test_isis_snmp.py b/tests/topotests/isis-snmp/test_isis_snmp.py
new file mode 100755
index 0000000000..1bcd0eefc6
--- /dev/null
+++ b/tests/topotests/isis-snmp/test_isis_snmp.py
@@ -0,0 +1,369 @@
+#!/usr/bin/env python
+
+#
+# test_isis_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_isis_snmp.py:
+
+ +---------+ 45.0.0.0/24 +---------+
+ | | rt4-eth1 | |
+ | RT4 +----------------+ RT5 |
+ | | rt5-eth1| |
+ +---------+ +---------+
+ rt4-eth0| |rt5-eth0
+ | |
+ 14.0.0.0/24| |25.0.0.0/24
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | 1.1.1.1 | | 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth1| |rt2-eth1
+ | |
+ | |
+ 13.0.0.0/24| +---------+ |23.0.0.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth1| |rt3-eth2
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.snmptest import SnmpTester
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce3", "r1", "r2", "r3", "r4", "r5"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r5"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # skip tests is SNMP not installed
+ if not os.path.isfile("/usr/sbin/snmpd"):
+ error_msg = "SNMP not installed - skipping"
+ pytest.skip(error_msg)
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start the following in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP, os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap",
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+def router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+def generate_oid(numoids, index1, index2):
+ if numoids == 1:
+ oid = "{}".format(index1)
+ else:
+ oid = "{}.{}".format(index1, index2)
+ return oid
+
+
+def test_isis_convergence():
+ logger.info("Test: check ISIS adjacencies")
+ tgen = get_topogen()
+
+ for rname in ["r1", "r2", "r3", "r4", "r5"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "show_yang_interface_isis_adjacencies.ref")
+
+def test_r1_scalar_snmp():
+ "Wait for protocol convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid('isisSysVersion', "one(1)")
+ assert r1_snmp.test_oid('isisSysLevelType', "level1and2(3)")
+ assert r1_snmp.test_oid('isisSysID',"00 00 00 00 00 01")
+ assert r1_snmp.test_oid('isisSysMaxPathSplits',"32")
+ assert r1_snmp.test_oid('isisSysMaxLSPGenInt',"900 seconds")
+ assert r1_snmp.test_oid('isisSysAdminState',"on(1)")
+ assert r1_snmp.test_oid('isisSysMaxAge',"1200 seconds")
+ assert r1_snmp.test_oid('isisSysProtSupported',"07 5 6 7")
+
+ r2 = tgen.net.get("r2")
+ r2_snmp = SnmpTester(r2, "2.2.2.2", "public", "2c")
+
+ assert r2_snmp.test_oid('isisSysVersion', "one(1)")
+ assert r2_snmp.test_oid('isisSysLevelType', "level1and2(3)")
+ assert r2_snmp.test_oid('isisSysID',"00 00 00 00 00 02")
+ assert r2_snmp.test_oid('isisSysMaxPathSplits',"32")
+ assert r2_snmp.test_oid('isisSysMaxLSPGenInt',"900 seconds")
+ assert r2_snmp.test_oid('isisSysAdminState',"on(1)")
+ assert r2_snmp.test_oid('isisSysMaxAge',"1200 seconds")
+ assert r2_snmp.test_oid('isisSysProtSupported',"07 5 6 7")
+
+
+circtable_test = {
+ "isisCircAdminState": ["on(1)", "on(1)", "on(1)"],
+ "isisCircExistState": ["active(1)", "active(1)", "active(1)"],
+ "isisCircType": ["broadcast(1)", "ptToPt(2)", "staticIn(3)"],
+ "isisCircExtDomain": ["false(2)", "false(2)", "false(2)"],
+ "isisCircLevelType": ["level1(1)", "level1(1)", "level1and2(3)"],
+ "isisCircPassiveCircuit": ["false(2)", "false(2)", "true(1)"],
+ "isisCircMeshGroupEnabled": ["inactive(1)", "inactive(1)", "inactive(1)"],
+ "isisCircSmallHellos": ["false(2)", "false(2)", "false(2)"],
+ "isisCirc3WayEnabled": ["false(2)", "false(2)", "false(2)"],
+ }
+
+def test_r1_isisCircTable():
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1r = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(1,1,0))
+ oids.append(generate_oid(1,2,0))
+ oids.append(generate_oid(1,3,0))
+
+ # check items
+ for item in circtable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, circtable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, circtable_test[item], oids), assertmsg
+
+circleveltable_test = {
+ "isisCircLevelMetric": ["10", "10", "10", "10"],
+ "isisCircLevelWideMetric": ["10", "10", "0", "0"],
+ "isisCircLevelISPriority": ["64", "64", "64", "64"],
+ "isisCircLevelHelloMultiplier": ["10", "10", "10", "10"],
+ "isisCircLevelHelloTimer": ["3000 milliseconds", "3000 milliseconds", "3000 milliseconds", "3000 milliseconds"],
+ "isisCircLevelMinLSPRetransInt": ["1 seconds", "1 seconds", "0 seconds", "0 seconds"],
+ }
+
+def test_r1_isislevelCircTable():
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1r = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(2,1,"area"))
+ oids.append(generate_oid(2,2,"area"))
+ oids.append(generate_oid(2,3,"area"))
+ oids.append(generate_oid(2,3,"domain"))
+
+ # check items
+ for item in circleveltable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, circleveltable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, circleveltable_test[item], oids), assertmsg
+
+
+adjtable_test = {
+ "isisISAdjState": ["up(3)", "up(3)"],
+ "isisISAdj3WayState": ["down(2)", "up(0)"],
+ "isisISAdjNeighSysType": ["l1IntermediateSystem(1)", "l1IntermediateSystem(1)"],
+ "isisISAdjNeighSysID": ["00 00 00 00 00 04", "00 00 00 00 00 03"],
+ "isisISAdjUsage": ["0", "level1(1)"],
+ "isisISAdjNeighPriority": ["64", "0"],
+}
+
+adjtable_down_test = {
+ "isisISAdjState": ["up(3)"],
+ "isisISAdj3WayState": ["down(2)"],
+ "isisISAdjNeighSysType": ["l1IntermediateSystem(1)"],
+ "isisISAdjNeighSysID": ["00 00 00 00 00 04"],
+ "isisISAdjUsage": ["0"],
+ "isisISAdjNeighPriority": ["64"],
+}
+
+def test_r1_isisAdjTable():
+ "check ISIS Adjacency Table"
+ tgen = get_topogen()
+ r1 = tgen.net.get("r1")
+ r1_cmd = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(2,1,1))
+ oids.append(generate_oid(2,2,1))
+
+ oids_down = []
+ oids_down.append(generate_oid(2,1,1))
+
+ # check items
+ for item in adjtable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, adjtable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, adjtable_test[item], oids), assertmsg
+
+
+ # shutdown interface and one adjacency should be removed
+ "check ISIS adjacency is removed when interface is shutdown"
+ r1_cmd.vtysh_cmd("conf t\ninterface r1-eth1\nshutdown")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ for item in adjtable_down_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, adjtable_down_test[item], oids_down, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, adjtable_down_test[item], oids_down), assertmsg
+
+ # no shutdown interface and adjacency should be restored
+ r1_cmd.vtysh_cmd("conf t\ninterface r1-eth1\nno shutdown")
+
+
+# Memory leak test template
+# disabling memory leak
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ldp-snmp/ce1/zebra.conf b/tests/topotests/ldp-snmp/ce1/zebra.conf
new file mode 100644
index 0000000000..6f165e2724
--- /dev/null
+++ b/tests/topotests/ldp-snmp/ce1/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface ce1-eth0
+ ip address 172.16.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/ce2/zebra.conf b/tests/topotests/ldp-snmp/ce2/zebra.conf
new file mode 100644
index 0000000000..ac02d0f9a4
--- /dev/null
+++ b/tests/topotests/ldp-snmp/ce2/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface ce2-eth0
+ ip address 172.16.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/ce3/zebra.conf b/tests/topotests/ldp-snmp/ce3/zebra.conf
new file mode 100644
index 0000000000..c6a5824d15
--- /dev/null
+++ b/tests/topotests/ldp-snmp/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/r1/isisd.conf b/tests/topotests/ldp-snmp/r1/isisd.conf
new file mode 100644
index 0000000000..da2970d94e
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/isisd.conf
@@ -0,0 +1,27 @@
+hostname r1
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r1-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r1-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp-snmp/r1/ldpd.conf b/tests/topotests/ldp-snmp/r1/ldpd.conf
new file mode 100644
index 0000000000..01fc039b09
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r1
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth1
+ !
+ interface r1-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r1-eth0
+ !
+ member pseudowire r1-mpw0
+ neighbor lsr-id 2.2.2.2
+ pw-id 100
+ !
+!
+line vty
+!
+agentx
+!
diff --git a/tests/topotests/ldp-snmp/r1/show_ip_route.ref b/tests/topotests/ldp-snmp/r1/show_ip_route.ref
new file mode 100644
index 0000000000..b1a55ba103
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_interface_detail.ref b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail.ref
new file mode 100644
index 0000000000..d8fb27af8c
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..f77d65ebc1
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..f77d65ebc1
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync.ref b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync.ref
new file mode 100644
index 0000000000..b699e8c145
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..c28cd4cc7d
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r1-eth1":{
+ "Interface":true
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..c63bbea77f
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_l2vpn_binding.ref b/tests/topotests/ldp-snmp/r1/show_l2vpn_binding.ref
new file mode 100644
index 0000000000..b3de7e2c66
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "2.2.2.2: 100":{
+ "destination":"2.2.2.2",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_l2vpn_vc.ref b/tests/topotests/ldp-snmp/r1/show_l2vpn_vc.ref
new file mode 100644
index 0000000000..29e9df1089
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r1-mpw0":{
+ "peerId":"2.2.2.2",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_ldp_binding.ref b/tests/topotests/ldp-snmp/r1/show_ldp_binding.ref
new file mode 100644
index 0000000000..b3a12ec53f
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_ldp_discovery.ref b/tests/topotests/ldp-snmp/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000000..9301e60c67
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"targeted",
+ "peer":"2.2.2.2",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r1-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync.ref
new file mode 100644
index 0000000000..54d015fef9
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..2232069f68
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-snmp/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000000..40d8ebeb90
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-snmp/r1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..6138d03672
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r1-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r1-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r1/snmpd.conf b/tests/topotests/ldp-snmp/r1/snmpd.conf
new file mode 100644
index 0000000000..b37911da36
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/ldp-snmp/r1/zebra.conf b/tests/topotests/ldp-snmp/r1/zebra.conf
new file mode 100644
index 0000000000..ea047355ad
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+debug zebra kernel
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra nht
+debug zebra pseudowires
+debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to s1
+!
+interface r1-eth1
+ description to s4
+ ip address 10.0.1.1/24
+!
+interface r1-eth2
+ description to s5
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/r2/isisd.conf b/tests/topotests/ldp-snmp/r2/isisd.conf
new file mode 100644
index 0000000000..b29a2b93ee
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/isisd.conf
@@ -0,0 +1,28 @@
+hostname r2
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r2-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r2-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
diff --git a/tests/topotests/ldp-snmp/r2/ldpd.conf b/tests/topotests/ldp-snmp/r2/ldpd.conf
new file mode 100644
index 0000000000..c93e1a6ac5
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r2
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r2-eth0
+ !
+ member pseudowire r2-mpw0
+ neighbor lsr-id 1.1.1.1
+ pw-id 100
+ !
+!
+line vty
+!
+!agentx
+!
diff --git a/tests/topotests/ldp-snmp/r2/ospfd.conf b/tests/topotests/ldp-snmp/r2/ospfd.conf
new file mode 100644
index 0000000000..f93f6aed56
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+debug ospf zebra interface
+debug ospf ldp-sync
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf mpls ldp-sync holddown 300
+!
+interface r2-eth2
+ ip ospf network point-to-point
+ no ip ospf mpls ldp-sync
+!
diff --git a/tests/topotests/ldp-snmp/r2/show_ip_route.ref b/tests/topotests/ldp-snmp/r2/show_ip_route.ref
new file mode 100644
index 0000000000..04f141aba4
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_interface_detail.ref b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail.ref
new file mode 100644
index 0000000000..844aa9402a
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..821ec70ba5
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..821ec70ba5
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync.ref b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync.ref
new file mode 100644
index 0000000000..433d89bd16
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..2f3eae47c8
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..c3d97a3c73
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r2-eth1":{
+ "Interface":true
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_l2vpn_binding.ref b/tests/topotests/ldp-snmp/r2/show_l2vpn_binding.ref
new file mode 100644
index 0000000000..42c5a1cbd9
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "1.1.1.1: 100":{
+ "destination":"1.1.1.1",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_l2vpn_vc.ref b/tests/topotests/ldp-snmp/r2/show_l2vpn_vc.ref
new file mode 100644
index 0000000000..942ed23a1e
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r2-mpw0":{
+ "peerId":"1.1.1.1",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_binding.ref b/tests/topotests/ldp-snmp/r2/show_ldp_binding.ref
new file mode 100644
index 0000000000..c641fb47e6
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_discovery.ref b/tests/topotests/ldp-snmp/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000000..26801acade
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"targeted",
+ "peer":"1.1.1.1",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync.ref
new file mode 100644
index 0000000000..f2b24d7d62
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..b5508dd35c
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..f2b24d7d62
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-snmp/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000000..eed35289ea
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-snmp/r2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..4dd6ddd76b
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r2-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r2-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r2/snmpd.conf b/tests/topotests/ldp-snmp/r2/snmpd.conf
new file mode 100644
index 0000000000..0f779b8b91
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/snmpd.conf
@@ -0,0 +1,15 @@
+agentAddress udp:2.2.2.2:161
+
+com2sec public 2.2.2.2 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
diff --git a/tests/topotests/ldp-snmp/r2/zebra.conf b/tests/topotests/ldp-snmp/r2/zebra.conf
new file mode 100644
index 0000000000..c244442876
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r2
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+debug zebra pseudowires
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to s2
+!
+interface r2-eth1
+ description to s4
+ ip address 10.0.1.2/24
+!
+interface r2-eth2
+ description to s6
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/r3/isisd.conf b/tests/topotests/ldp-snmp/r3/isisd.conf
new file mode 100644
index 0000000000..4c8499f23d
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/isisd.conf
@@ -0,0 +1,29 @@
+hostname r3
+log file isisd.log
+debug isis adj-packets
+debug isis events
+debug isis update-packets
+debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
+interface r3-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp-snmp/r3/ldpd.conf b/tests/topotests/ldp-snmp/r3/ldpd.conf
new file mode 100644
index 0000000000..b7eeb258f1
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/ldpd.conf
@@ -0,0 +1,27 @@
+hostname r3
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
+!agentx
+!
diff --git a/tests/topotests/ldp-snmp/r3/show_ip_route.ref b/tests/topotests/ldp-snmp/r3/show_ip_route.ref
new file mode 100644
index 0000000000..22504046ed
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_interface_detail.ref b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail.ref
new file mode 100644
index 0000000000..e323f61f25
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..e323f61f25
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..e323f61f25
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync.ref b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync.ref
new file mode 100644
index 0000000000..9cb70a4758
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..9cb70a4758
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000000..9cb70a4758
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_l2vpn_binding.ref b/tests/topotests/ldp-snmp/r3/show_l2vpn_binding.ref
new file mode 100644
index 0000000000..2c63c08510
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_l2vpn_binding.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_l2vpn_vc.ref b/tests/topotests/ldp-snmp/r3/show_l2vpn_vc.ref
new file mode 100644
index 0000000000..2c63c08510
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_l2vpn_vc.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_ldp_binding.ref b/tests/topotests/ldp-snmp/r3/show_ldp_binding.ref
new file mode 100644
index 0000000000..e54bd6e755
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_ldp_discovery.ref b/tests/topotests/ldp-snmp/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000000..42fa98d4da
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r3-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync.ref
new file mode 100644
index 0000000000..73261830c9
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000000..73261830c9
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-snmp/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000000..5c482da697
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp-snmp/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp-snmp/r3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000000..0922192361
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r3-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r3-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp-snmp/r3/zebra.conf b/tests/topotests/ldp-snmp/r3/zebra.conf
new file mode 100644
index 0000000000..b1919bd296
--- /dev/null
+++ b/tests/topotests/ldp-snmp/r3/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r3
+!
+debug zebra rib detailed
+debug zebra dplane detailed
+debug zebra kernel
+debug zebra nht
+debug zebra pseudowires
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to s3
+!
+interface r3-eth1
+ description to s5
+ ip address 10.0.2.3/24
+!
+interface r3-eth2
+ description to s6
+ ip address 10.0.3.3/24
+!
+!!interface r3-eth3
+!! description to s4
+!! ip address 10.0.1.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py
new file mode 100644
index 0000000000..4144f9b261
--- /dev/null
+++ b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py
@@ -0,0 +1,421 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ldp_vpls_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---------+ +---------+
+ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
+ | |
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ 10.0.1.0/24 +---------+
+ | |rt1-eth1 | |
+ | RT1 +----------------+ RT2 |
+ | 1.1.1.1 | rt2-eth1| 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth2| |rt2-eth2
+ | |
+ | |
+ 10.0.2.0/24| +---------+ |10.0.3.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth2| |rt3-eth1
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.snmptest import SnmpTester
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start isisd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)),
+ "-M snmp"
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP, os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap"
+ )
+
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=320, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_isis_convergence():
+ logger.info("Test: check ISIS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ # TODO: disabling this check to avoid 'snmpd not running' errors
+ #if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ # TODO: disabling this check to avoid 'snmpd not running' errors
+ #if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ #if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_r1_ldp_lsr_objects():
+ "Test mplsLdpLsrObjects objects"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid('mplsLdpLsrId', "01 01 01 01")
+ assert r1_snmp.test_oid('mplsLdpLsrLoopDetectionCapable', 'none(1)')
+
+
+def test_r1_ldp_entity_table():
+ "Test mplsLdpEntityTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityLdpId', ['1.1.1.1:0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityIndex', ['1'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityProtocolVersion', ['1'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityAdminStatus', ['enable(1)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityOperStatus', ['enabled(2)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityTcpPort', ['646'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityUdpDscPort', ['646'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityMaxPduLength', ['4096 octets'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityKeepAliveHoldTimer', ['180 seconds'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityHelloHoldTimer', ['0 seconds'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityInitSessionThreshold', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityLabelDistMethod', ['downstreamUnsolicited(2)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityLabelRetentionMode', ['liberal(2)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityPathVectorLimit', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityHopCountLimit', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityTransportAddrKind', ['loopback(2)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityTargetPeer', ['true(1)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityTargetPeerAddrType', ['ipv4(1)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityTargetPeerAddr', ['01 01 01 01'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityLabelType', ['generic(1)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityDiscontinuityTime', ['(0) 0:00:00.00'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStorageType', ['nonVolatile(3)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityRowStatus', ['createAndGo(4)'])
+
+
+def test_r1_ldp_entity_stats_table():
+ "Test mplsLdpEntityStatsTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsSessionAttempts', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsSessionRejectedNoHelloErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsSessionRejectedAdErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsSessionRejectedMaxPduErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsSessionRejectedLRErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsBadLdpIdentifierErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsBadPduLengthErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsBadMessageLengthErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsBadTlvLengthErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsMalformedTlvValueErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsKeepAliveTimerExpErrors', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsShutdownReceivedNotifications', ['0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpEntityStatsShutdownSentNotifications', ['0'])
+
+
+def test_r1_ldp_peer_table():
+ "Test mplsLdpPeerTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpPeerLdpId', ['2.2.2.2:0', '3.3.3.3:0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpPeerLabelDistMethod',
+ ['downstreamUnsolicited(2)', 'downstreamUnsolicited(2)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpPeerPathVectorLimit', ['0', '0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpPeerTransportAddrType', ['ipv4(1)', 'ipv4(1)'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpPeerTransportAddr', ['02 02 02 02', '03 03 03 03'])
+
+
+def test_r1_ldp_session_table():
+ "Test mplsLdpSessionTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk('mplsLdpSessionState',
+ ['operational(5)', 'operational(5)'])
+ assert r1_snmp.test_oid_walk('mplsLdpSessionRole',
+ ['passive(3)', 'passive(3)'])
+ assert r1_snmp.test_oid_walk('mplsLdpSessionProtocolVersion',
+ ['1', '1'])
+ assert r1_snmp.test_oid_walk('mplsLdpSessionKeepAliveTime',
+ ['180 seconds', '180 seconds'])
+ assert r1_snmp.test_oid_walk('mplsLdpSessionMaxPduLength',
+ ['4096 octets', '4096 octets'])
+ assert r1_snmp.test_oid_walk('mplsLdpSessionDiscontinuityTime',
+ ['(0) 0:00:00.00', '(0) 0:00:00.00'])
+
+
+def test_r1_ldp_session_stats_table():
+ "Test mplsLdpSessionStatsTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpSessionStatsUnknownMesTypeErrors', ['0', '0'])
+ assert r1_snmp.test_oid_walk(
+ 'mplsLdpSessionStatsUnknownTlvErrors', ['0', '0'])
+
+
+def test_r1_ldp_hello_adjacency_table():
+ "Test mplsLdpHelloAdjacencyTable"
+ tgen = get_topogen()
+
+ r1 = tgen.net.get("r1")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyIndex',
+ ['1', '2', '1'])
+ assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyHoldTime',
+ ['15', '45', '15'])
+ assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyType',
+ ['link(1)', 'targeted(2)', 'link(1)'])
+
+
+# Memory leak test template
+# disabling memory leak
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index 087c35981d..0bc6547994 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -42,12 +42,13 @@ show zebra client summary
show zebra router table summary
show ip nht vrf all
show ipv6 nht vrf all
+show ip route vrf all
+show ipv6 route vrf all
show nexthop-group rib
show route-map
show memory
show interface vrf all
show vrf
-show zebra fpm stats
show work-queues
show debugging hashtable
show running-config
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index b98c001e7d..1461e0f296 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -97,7 +97,7 @@ class Vtysh(object):
args = ["-c", command]
return self._call(args, stdin, stdout, stderr)
- def __call__(self, command):
+ def __call__(self, command, stdouts=None):
"""
Call a CLI command (e.g. "show running-config")
@@ -107,6 +107,8 @@ class Vtysh(object):
proc = self._call_cmd(command, stdout=subprocess.PIPE)
stdout, stderr = proc.communicate()
if proc.wait() != 0:
+ if stdouts is not None:
+ stdouts.append(stdout.decode('UTF-8'))
raise VtyshException(
'vtysh returned status %d for command "%s"' % (proc.returncode, command)
)
@@ -2006,9 +2008,10 @@ if __name__ == "__main__":
# frr(config-if)# no ip ospf authentication
# frr(config-if)#
+ stdouts = []
while True:
try:
- vtysh(["configure"] + cmd)
+ vtysh(["configure"] + cmd, stdouts)
except VtyshException:
@@ -2024,6 +2027,10 @@ if __name__ == "__main__":
'"%s" we failed to remove this command',
" -- ".join(original_cmd),
)
+ # Log first error msg for original_cmd
+ if stdouts:
+ log.error(stdouts[0])
+ reload_ok = False
break
new_last_arg = last_arg[0:-1]
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index 53067c43f4..81c9770e55 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -107,7 +107,7 @@ sub scan_file {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/agentx\.c$/) {
- $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
+ $protocol = "VTYSH_ISISD|VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
}
elsif ($file =~ /lib\/nexthop_group\.c$/) {
$protocol = "VTYSH_NH_GROUP";
diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang
index 5b434162d0..d21ff5068a 100644
--- a/yang/frr-bfdd.yang
+++ b/yang/frr-bfdd.yang
@@ -185,7 +185,7 @@ module frr-bfdd {
leaf administrative-down {
type boolean;
- default true;
+ default false;
description "Disables or enables the session administratively";
}
@@ -210,7 +210,14 @@ module frr-bfdd {
type uint32;
units microseconds;
default 50000;
- description "Minimum desired control packet transmission interval";
+ description "Minimum desired echo packet transmission interval";
+ }
+
+ leaf required-echo-receive-interval {
+ type uint32;
+ units microseconds;
+ default 50000;
+ description "Minimum required echo packet receive interval";
}
}
diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang
index eb84dd7460..9a864213ee 100644
--- a/yang/frr-filter.yang
+++ b/yang/frr-filter.yang
@@ -292,8 +292,6 @@ module frr-filter {
mandatory true;
case ipv4-prefix {
- when "../type = 'ipv4'";
-
leaf ipv4-prefix {
description "Configure IPv4 prefix to match";
type inet:ipv4-prefix;
@@ -318,8 +316,6 @@ module frr-filter {
}
}
case ipv6-prefix {
- when "../type = 'ipv6'";
-
leaf ipv6-prefix {
description "Configure IPv6 prefix to match";
type inet:ipv6-prefix;
diff --git a/zebra/connected.c b/zebra/connected.c
index c885c533e6..dd8fab5e4e 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -321,11 +321,11 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
if (IPV4_ADDR_SAME(addr, dest))
flog_warn(
EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER,
- "warning: interface %s has same local and peer address %pI4, routing protocols may malfunction",
+ "interface %s has same local and peer address %pI4, routing protocols may malfunction",
ifp->name, addr);
} else {
zlog_debug(
- "warning: %s called for interface %s with peer flag set, but no peer address supplied",
+ "%s called for interface %s with peer flag set, but no peer address supplied",
__func__, ifp->name);
UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
}
@@ -335,7 +335,7 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN)
&& if_is_pointopoint(ifp))
zlog_debug(
- "warning: PtP interface %s with addr %pI4/%d needs a peer address",
+ "PtP interface %s with addr %pI4/%d needs a peer address",
ifp->name, addr, prefixlen);
/* Label of this address. */
@@ -503,7 +503,7 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
} else {
if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) {
zlog_debug(
- "warning: %s called for interface %s with peer flag set, but no peer address supplied",
+ "%s called for interface %s with peer flag set, but no peer address supplied",
__func__, ifp->name);
UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
}
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index b0f124ed55..ac60d09ecc 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -542,7 +542,7 @@ void zebra_interface_address_add_update(struct interface *ifp,
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
flog_warn(
EC_ZEBRA_ADVERTISING_UNUSABLE_ADDR,
- "WARNING: advertising address to clients that is not yet usable.");
+ "advertising address to clients that is not yet usable.");
zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 1);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 93961686fd..fdeef2c88c 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -2839,7 +2839,6 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
vlanid_t vid = 0;
struct in_addr vtep_ip;
int vid_present = 0, dst_present = 0;
- char buf[ETHER_ADDR_STRLEN];
char vid_buf[20];
char dst_buf[30];
bool sticky;
@@ -2918,11 +2917,10 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
}
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s nhg %d",
+ zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d",
nl_msg_type_to_str(h->nlmsg_type),
ndm->ndm_ifindex, vid_present ? vid_buf : "",
- ndm->ndm_state, ndm->ndm_flags,
- prefix_mac2str(&mac, buf, sizeof(buf)),
+ ndm->ndm_state, ndm->ndm_flags, &mac,
dst_present ? dst_buf : "", nhg_id);
/* The interface should exist. */
@@ -3123,7 +3121,6 @@ static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns,
char buf[256];
} req;
struct zebra_if *br_zif;
- char buf[ETHER_ADDR_STRLEN];
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
@@ -3142,11 +3139,10 @@ static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns,
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %s vid %u",
+ "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %pEA vid %u",
__func__, nl_family_to_str(req.ndm.ndm_family),
br_if->name, br_if->ifindex,
- vrf_id_to_name(br_if->vrf_id), br_if->vrf_id,
- prefix_mac2str(mac, buf, sizeof(buf)), vid);
+ vrf_id_to_name(br_if->vrf_id), br_if->vrf_id, mac, vid);
return netlink_request(&zns->netlink_cmd, &req);
}
@@ -3231,8 +3227,6 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
SET_IPADDR_V4(&vtep_ip);
if (IS_ZEBRA_DEBUG_KERNEL) {
- char ipbuf[PREFIX_STRLEN];
- char buf[ETHER_ADDR_STRLEN];
char vid_buf[20];
const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx);
@@ -3243,12 +3237,11 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
vid_buf[0] = '\0';
zlog_debug(
- "Tx %s family %s IF %s(%u)%s %sMAC %s dst %s nhg %u%s%s%s%s%s",
+ "Tx %s family %s IF %s(%u)%s %sMAC %pEA dst %pIA nhg %u%s%s%s%s%s",
nl_msg_type_to_str(cmd), nl_family_to_str(AF_BRIDGE),
dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx),
vid_buf, dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "",
- prefix_mac2str(mac, buf, sizeof(buf)),
- ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)), nhg_id,
+ mac, &vtep_ip, nhg_id,
(update_flags & DPLANE_MAC_REMOTE) ? " rem" : "",
(update_flags & DPLANE_MAC_WAS_STATIC) ? " clr_sync"
: "",
@@ -3309,7 +3302,6 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
struct ipaddr ip;
struct vrf *vrf;
char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
int mac_present = 0;
bool is_ext;
bool is_router;
@@ -3415,11 +3407,11 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "Rx %s family %s IF %s(%u) vrf %s(%u) IP %s MAC %s state 0x%x flags 0x%x ext_flags 0x%x",
+ "Rx %s family %s IF %s(%u) vrf %s(%u) IP %pIA MAC %s state 0x%x flags 0x%x ext_flags 0x%x",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(ndm->ndm_family), ifp->name,
ndm->ndm_ifindex, VRF_LOGNAME(vrf), ifp->vrf_id,
- ipaddr2str(&ip, buf2, sizeof(buf2)),
+ &ip,
mac_present
? prefix_mac2str(&mac, buf, sizeof(buf))
: "",
@@ -3451,11 +3443,11 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
}
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("Rx %s family %s IF %s(%u) vrf %s(%u) IP %s",
+ zlog_debug("Rx %s family %s IF %s(%u) vrf %s(%u) IP %pIA",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(ndm->ndm_family), ifp->name,
ndm->ndm_ifindex, VRF_LOGNAME(vrf), ifp->vrf_id,
- ipaddr2str(&ip, buf2, sizeof(buf2)));
+ &ip);
/* Process the delete - it may result in re-adding the neighbor if it is
* a valid "remote" neighbor.
@@ -3582,14 +3574,11 @@ static int netlink_request_specific_neigh_in_vlan(struct zebra_ns *zns,
nl_attr_put(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
- if (IS_ZEBRA_DEBUG_KERNEL) {
- char buf[INET6_ADDRSTRLEN];
-
- zlog_debug("%s: Tx %s family %s IF %u IP %s flags 0x%x",
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: Tx %s family %s IF %u IP %pIA flags 0x%x",
__func__, nl_msg_type_to_str(type),
- nl_family_to_str(req.ndm.ndm_family), ifindex,
- ipaddr2str(ip, buf, sizeof(buf)), req.n.nlmsg_flags);
- }
+ nl_family_to_str(req.ndm.ndm_family), ifindex, ip,
+ req.n.nlmsg_flags);
return netlink_request(&zns->netlink_cmd, &req);
}
@@ -3600,7 +3589,6 @@ int netlink_neigh_read_specific_ip(struct ipaddr *ip,
int ret = 0;
struct zebra_ns *zns;
struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vlan_if->vrf_id);
- char buf[INET6_ADDRSTRLEN];
struct zebra_dplane_info dp_info;
zns = zvrf->zns;
@@ -3608,9 +3596,8 @@ int netlink_neigh_read_specific_ip(struct ipaddr *ip,
zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s: neigh request IF %s(%u) IP %s vrf %s(%u)",
- __func__, vlan_if->name, vlan_if->ifindex,
- ipaddr2str(ip, buf, sizeof(buf)),
+ zlog_debug("%s: neigh request IF %s(%u) IP %pIA vrf %s(%u)",
+ __func__, vlan_if->name, vlan_if->ifindex, ip,
vrf_id_to_name(vlan_if->vrf_id), vlan_if->vrf_id);
ret = netlink_request_specific_neigh_in_vlan(zns, RTM_GETNEIGH, ip,
@@ -3702,18 +3689,12 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
if (update_flags & DPLANE_NEIGH_SET_STATIC)
ext_flags |= NTF_E_MH_PEER_SYNC;
}
- if (IS_ZEBRA_DEBUG_KERNEL) {
- char buf[INET6_ADDRSTRLEN];
- char buf2[ETHER_ADDR_STRLEN];
-
+ if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x state 0x%x %sext_flags 0x%x",
+ "Tx %s family %s IF %s(%u) Neigh %pIA MAC %pEA flags 0x%x state 0x%x %sext_flags 0x%x",
nl_msg_type_to_str(cmd), nl_family_to_str(family),
dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx),
- ipaddr2str(ip, buf, sizeof(buf)),
- mac ? prefix_mac2str(mac, buf2, sizeof(buf2)) : "null",
- flags, state, ext ? "ext " : "", ext_flags);
- }
+ ip, mac, flags, state, ext ? "ext " : "", ext_flags);
return netlink_neigh_update_msg_encode(
ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state,
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 82546ce188..4e63d08aca 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -3539,15 +3539,9 @@ mac_update_common(enum dplane_op_e op,
int ret;
struct zebra_dplane_ctx *ctx = NULL;
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
-
- zlog_debug("init mac ctx %s: mac %s, ifp %s, vtep %s",
- dplane_op2str(op),
- prefix_mac2str(mac, buf1, sizeof(buf1)),
- ifp->name,
- inet_ntop(AF_INET, &vtep_ip, buf2, sizeof(buf2)));
- }
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("init mac ctx %s: mac %pEA, ifp %s, vtep %pI4",
+ dplane_op2str(op), mac, ifp->name, &vtep_ip);
ctx = dplane_ctx_alloc();
ctx->zd_op = op;
@@ -3725,14 +3719,9 @@ neigh_update_internal(enum dplane_op_e op,
struct zebra_dplane_ctx *ctx = NULL;
struct zebra_ns *zns;
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
-
- zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s",
- dplane_op2str(op), ifp->name,
- prefix_mac2str(mac, buf1, sizeof(buf1)),
- ipaddr2str(ip, buf2, sizeof(buf2)));
- }
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("init neigh ctx %s: ifp %s, mac %pEA, ip %pIA",
+ dplane_op2str(op), ifp->name, mac, ip);
ctx = dplane_ctx_alloc();
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
index d7076ccce6..27a5a07e48 100644
--- a/zebra/zebra_evpn.c
+++ b/zebra/zebra_evpn.c
@@ -447,8 +447,6 @@ int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn,
struct ipaddr *ip)
{
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
zebra_neigh_t *n = NULL;
zebra_mac_t *mac = NULL;
@@ -461,11 +459,8 @@ int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn,
mac = zebra_evpn_mac_lookup(zevpn, &n->emac);
if (!mac) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u",
- prefix_mac2str(&n->emac,
- buf1, sizeof(buf1)),
- ipaddr2str(ip, buf2, sizeof(buf2)),
- zevpn->vni);
+ zlog_debug("MAC %pEA doesn't exist for neigh %pIA on VNI %u",
+ &n->emac, ip, zevpn->vni);
return -1;
}
@@ -476,10 +471,9 @@ int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn,
/* only need to delete the entry from bgp if we sent it before */
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ "%u:SVI %s(%u) VNI %u, sending GW MAC %pEA IP %pIA del to BGP",
ifp->vrf_id, ifp->name, ifp->ifindex, zevpn->vni,
- prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
- ipaddr2str(ip, buf2, sizeof(buf2)));
+ &n->emac, ip);
/* Remove neighbor from BGP. */
zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac,
@@ -1321,7 +1315,6 @@ zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
uint8_t flags, uint32_t seq, esi_t *esi)
{
struct sync_mac_ip_ctx ctx;
- char macbuf[ETHER_ADDR_STRLEN];
char ipbuf[INET6_ADDRSTRLEN];
bool sticky;
bool remote_gw;
@@ -1334,9 +1327,9 @@ zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH
|| IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
- "Ignore sync-macip vni %u mac %s%s%s%s%s",
+ "Ignore sync-macip vni %u mac %pEA%s%s%s%s",
zevpn->vni,
- prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
@@ -1455,7 +1448,6 @@ void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
struct zebra_ns *zns;
struct zebra_l2info_vxlan *vxl;
struct zebra_vrf *zvrf;
- char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
/* Locate EVPN hash entry - expected to exist. */
@@ -1485,9 +1477,8 @@ void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
if (n && !mac) {
zlog_warn(
- "Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni);
+ "Failed to locate MAC %pEA for neigh %pIA VNI %u upon remote MACIP DEL",
+ macaddr, ipaddr, vni);
return;
}
@@ -1503,8 +1494,8 @@ void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
&& CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
zlog_warn(
- "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
- vni, prefix_mac2str(macaddr, buf, sizeof(buf)),
+ "Ignore remote MACIP DEL VNI %u MAC %pEA%s%s as MAC is already configured as gateway MAC",
+ vni, macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : "");
return;
@@ -1525,11 +1516,8 @@ void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr,
&& CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry",
- __func__,
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- mac->flags);
+ "%s: MAC %pEA (flags 0x%x) is remote and duplicate, read kernel for local entry",
+ __func__, macaddr, mac->flags);
macfdb_read_specific_mac(zns, zif->brslave_info.br_if,
macaddr, vxl->access_vlan);
}
diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c
index 142a199bd7..b36e8034b7 100644
--- a/zebra/zebra_evpn_mac.c
+++ b/zebra/zebra_evpn_mac.c
@@ -384,7 +384,6 @@ static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t)
zebra_evpn_t *zevpn = NULL;
struct listnode *node = NULL;
zebra_neigh_t *nbr = NULL;
- char buf[ETHER_ADDR_STRLEN];
mac = THREAD_ARG(t);
@@ -405,9 +404,8 @@ static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t)
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: duplicate addr mac %s flags %slearn count %u host count %u auto recovery expired",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "%s: duplicate addr mac %pEA flags %slearn count %u host count %u auto recovery expired",
+ __func__, &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)),
mac->dad_count, listcount(mac->neigh_list));
@@ -466,8 +464,6 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
zebra_neigh_t *nbr;
struct listnode *node = NULL;
struct timeval elapsed = {0, 0};
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
bool reset_params = false;
if (!(zebra_evpn_do_dup_addr_detect(zvrf) && do_dad))
@@ -482,9 +478,8 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: duplicate addr MAC %s flags %sskip update to client, learn count %u recover time %u",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "%s: duplicate addr MAC %pEA flags %sskip update to client, learn count %u recover time %u",
+ __func__, &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)),
mac->dad_count, zvrf->dad_freeze_time);
@@ -520,9 +515,8 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: duplicate addr MAC %s flags %sdetection time passed, reset learn count %u",
- __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "%s: duplicate addr MAC %pEA flags %sdetection time passed, reset learn count %u",
+ __func__, &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)),
mac->dad_count);
@@ -552,9 +546,8 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
if (mac->dad_count >= zvrf->dad_max_moves) {
flog_warn(EC_ZEBRA_DUP_MAC_DETECTED,
- "VNI %u: MAC %s detected as duplicate during %s VTEP %pI4",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "VNI %u: MAC %pEA detected as duplicate during %s VTEP %pI4",
+ mac->zevpn->vni, &mac->macaddr,
is_local ? "local update, last" :
"remote update, from", &vtep_ip);
@@ -577,11 +570,8 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
nbr->dad_dup_detect_time = monotime(NULL);
flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr,
- buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ "VNI %u: MAC %pEA IP %pIA detected as duplicate during %s update, inherit duplicate from MAC",
+ mac->zevpn->vni, &mac->macaddr, &nbr->ip,
is_local ? "local" : "remote");
}
@@ -592,10 +582,8 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: duplicate addr MAC %s flags %sauto recovery time %u start",
- __func__,
- prefix_mac2str(&mac->macaddr, buf,
- sizeof(buf)),
+ "%s: duplicate addr MAC %pEA flags %sauto recovery time %u start",
+ __func__, &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(
mac, mac_buf, sizeof(mac_buf)),
zvrf->dad_freeze_time);
@@ -1004,8 +992,6 @@ int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
uint32_t seq, int state,
struct zebra_evpn_es *es, uint16_t cmd)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
int ipa_len;
struct zserv *client = NULL;
struct stream *s = NULL;
@@ -1050,12 +1036,11 @@ int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr,
char flag_buf[MACIP_BUF_SIZE];
zlog_debug(
- "Send MACIP %s f %s MAC %s IP %s seq %u L2-VNI %u ESI %s to %s",
+ "Send MACIP %s f %s MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s",
(cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
zclient_evpn_dump_macip_flags(flags, flag_buf,
sizeof(flag_buf)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni,
+ macaddr, ip, seq, vni,
es ? es->esi_str : "-",
zebra_route_string(client->proto));
}
@@ -1129,11 +1114,10 @@ zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr)
mac->uptime = monotime(NULL);
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
- char buf[ETHER_ADDR_STRLEN];
char mac_buf[MAC_BUF_SIZE];
- zlog_debug("%s: MAC %s flags %s", __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ zlog_debug("%s: MAC %pEA flags %s", __func__,
+ &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
}
@@ -1148,11 +1132,10 @@ int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac)
zebra_mac_t *tmp_mac;
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
- char buf[ETHER_ADDR_STRLEN];
char mac_buf[MAC_BUF_SIZE];
- zlog_debug("%s: MAC %s flags %s", __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ zlog_debug("%s: MAC %pEA flags %s", __func__,
+ &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
}
@@ -1201,12 +1184,11 @@ static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx,
else if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_AUTO)
&& !listcount(mac->neigh_list)) {
if (IS_ZEBRA_DEBUG_VXLAN) {
- char buf[ETHER_ADDR_STRLEN];
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: Del MAC %s flags %s", __func__,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "%s: Del MAC %pEA flags %s", __func__,
+ &mac->macaddr,
zebra_evpn_zebra_mac_flag_dump(
mac, mac_buf, sizeof(mac_buf)));
}
@@ -1471,7 +1453,6 @@ static int zebra_evpn_mac_hold_exp_cb(struct thread *t)
bool new_bgp_ready;
bool old_static;
bool new_static;
- char macbuf[ETHER_ADDR_STRLEN];
mac = THREAD_ARG(t);
/* the purpose of the hold timer is to age out the peer-active
@@ -1490,9 +1471,8 @@ static int zebra_evpn_mac_hold_exp_cb(struct thread *t)
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "sync-mac vni %u mac %s es %s %shold expired",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ "sync-mac vni %u mac %pEA es %s %shold expired",
+ mac->zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-",
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
@@ -1516,8 +1496,6 @@ static int zebra_evpn_mac_hold_exp_cb(struct thread *t)
static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac)
{
- char macbuf[ETHER_ADDR_STRLEN];
-
if (mac->hold_timer)
return;
@@ -1525,9 +1503,8 @@ static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac)
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "sync-mac vni %u mac %s es %s %shold started",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ "sync-mac vni %u mac %pEA es %s %shold started",
+ mac->zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-",
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
@@ -1538,8 +1515,6 @@ static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac)
void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac)
{
- char macbuf[ETHER_ADDR_STRLEN];
-
if (!mac->hold_timer)
return;
@@ -1547,9 +1522,8 @@ void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac)
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "sync-mac vni %u mac %s es %s %shold stopped",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ "sync-mac vni %u mac %pEA es %s %shold stopped",
+ mac->zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-",
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
@@ -1560,7 +1534,6 @@ void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac)
void zebra_evpn_sync_mac_del(zebra_mac_t *mac)
{
- char macbuf[ETHER_ADDR_STRLEN];
bool old_static;
bool new_static;
@@ -1568,9 +1541,8 @@ void zebra_evpn_sync_mac_del(zebra_mac_t *mac)
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "sync-mac del vni %u mac %s es %s seq %d f %s",
- mac->zevpn->vni,
- prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
+ "sync-mac del vni %u mac %pEA es %s seq %d f %s",
+ mac->zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)));
@@ -1595,7 +1567,6 @@ static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
struct ipaddr *ipaddr,
bool sync)
{
- char macbuf[ETHER_ADDR_STRLEN];
char ipbuf[INET6_ADDRSTRLEN];
uint32_t tmp_seq;
const char *n_type;
@@ -1620,11 +1591,10 @@ static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s-macip accept vni %u %s-mac %s%s%s lower seq %u f %s",
+ "%s-macip accept vni %u %s-mac %pEA%s%s lower seq %u f %s",
sync ? "sync" : "rem", zevpn->vni,
n_type,
- prefix_mac2str(&mac->macaddr, macbuf,
- sizeof(macbuf)),
+ &mac->macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
@@ -1641,10 +1611,9 @@ static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s-macip ignore vni %u %s-mac %s%s%s as existing has higher seq %u f %s",
+ "%s-macip ignore vni %u %s-mac %pEA%s%s as existing has higher seq %u f %s",
sync ? "sync" : "rem", zevpn->vni, n_type,
- prefix_mac2str(&mac->macaddr, macbuf,
- sizeof(macbuf)),
+ &mac->macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
@@ -1671,7 +1640,6 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
bool seq_change = false;
bool es_change = false;
uint32_t tmp_seq;
- char macbuf[ETHER_ADDR_STRLEN];
char ipbuf[INET6_ADDRSTRLEN];
bool old_local = false;
bool old_bgp_ready;
@@ -1719,10 +1687,8 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
if (sticky || remote_gw) {
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "Ignore sync-macip vni %u mac %s%s%s%s%s",
- zevpn->vni,
- prefix_mac2str(macaddr, macbuf,
- sizeof(macbuf)),
+ "Ignore sync-macip vni %u mac %pEA%s%s%s%s",
+ zevpn->vni, macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
@@ -1789,9 +1755,8 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
omac.flags = old_flags;
zlog_debug(
- "sync-mac vni %u mac %s old_f %snew_f %s",
- zevpn->vni,
- prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ "sync-mac vni %u mac %pEA old_f %snew_f %s",
+ zevpn->vni, macaddr,
zebra_evpn_zebra_mac_flag_dump(
&omac, omac_buf, sizeof(omac_buf)),
zebra_evpn_zebra_mac_flag_dump(
@@ -1834,9 +1799,9 @@ zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr,
if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
char mac_buf[MAC_BUF_SIZE];
- zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f %s%s%s",
- ctx->mac_created ? "created" : "updated", zevpn->vni,
- prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
+ zlog_debug("sync-mac %s vni %u mac %pEA es %s seq %d f %s%s%s",
+ ctx->mac_created ? "created" : "updated",
+ zevpn->vni, macaddr,
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)),
@@ -2000,7 +1965,6 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
struct in_addr vtep_ip, uint8_t flags,
uint32_t seq, esi_t *esi)
{
- char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
bool sticky;
bool remote_gw;
@@ -2023,9 +1987,8 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
&& CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC",
- zevpn->vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ "Ignore remote MACIP ADD VNI %u MAC %pEA%s%s as MAC is already configured as gateway MAC",
+ zevpn->vni, macaddr,
ipa_len ? " IP " : "",
ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1))
: "");
@@ -2050,10 +2013,8 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
mac = zebra_evpn_mac_add(zevpn, macaddr);
if (!mac) {
zlog_warn(
- "Failed to add MAC %s VNI %u Remote VTEP %pI4",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- zevpn->vni, &vtep_ip);
+ "Failed to add MAC %pEA VNI %u Remote VTEP %pI4",
+ macaddr, zevpn->vni, &vtep_ip);
return -1;
}
@@ -2112,10 +2073,8 @@ int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "sync-mac->remote vni %u mac %s es %s seq %d f %s",
- zevpn->vni,
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
+ "sync-mac->remote vni %u mac %pEA es %s seq %d f %s",
+ zevpn->vni, macaddr,
mac->es ? mac->es->esi_str : "-",
mac->loc_seq,
zebra_evpn_zebra_mac_flag_dump(
@@ -2173,7 +2132,6 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
bool sticky, bool local_inactive,
bool dp_static, zebra_mac_t *mac)
{
- char buf[ETHER_ADDR_STRLEN];
bool mac_sticky = false;
bool inform_client = false;
bool upd_neigh = false;
@@ -2194,9 +2152,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
if (!mac) {
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
- "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ "ADD %sMAC %pEA intf %s(%u) VID %u -> VNI %u%s",
+ sticky ? "sticky " : "", macaddr,
ifp->name, ifp->ifindex, vid, zevpn->vni,
local_inactive ? " local-inactive" : "");
@@ -2204,9 +2161,9 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
if (!mac) {
flog_err(
EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add MAC %s intf %s(%u) VID %u VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vid, zevpn->vni);
+ "Failed to add MAC %pEA intf %s(%u) VID %u VNI %u",
+ macaddr, ifp->name, ifp->ifindex, vid,
+ zevpn->vni);
return -1;
}
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
@@ -2219,9 +2176,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags %s",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ "UPD %sMAC %pEA intf %s(%u) VID %u -> VNI %u %scurFlags %s",
+ sticky ? "sticky " : "", macaddr,
ifp->name, ifp->ifindex, vid, zevpn->vni,
local_inactive ? "local-inactive " : "",
zebra_evpn_zebra_mac_flag_dump(
@@ -2254,13 +2210,11 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
&& dp_static == old_static && !es_change) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, "
+ " Add/Update %sMAC %pEA intf %s(%u) VID %u -> VNI %u%s, "
"entry exists and has not changed ",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
- ifp->name, ifp->ifindex, vid,
- zevpn->vni,
+ sticky ? "sticky " : "",
+ macaddr, ifp->name,
+ ifp->ifindex, vid, zevpn->vni,
local_inactive
? " local_inactive"
: "");
@@ -2315,9 +2269,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
flog_warn(
EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT,
- "MAC %s already learnt as remote sticky MAC behind VTEP %pI4 VNI %u",
- prefix_mac2str(macaddr, buf,
- sizeof(buf)),
+ "MAC %pEA already learnt as remote sticky MAC behind VTEP %pI4 VNI %u",
+ macaddr,
&mac->fwd_info.r_vtep_ip,
zevpn->vni);
return 0;
@@ -2382,9 +2335,8 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "local mac vni %u mac %s es %s seq %d f %s%s",
- zevpn->vni,
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ "local mac vni %u mac %pEA es %s seq %d f %s%s",
+ zevpn->vni, macaddr,
mac->es ? mac->es->esi_str : "", mac->loc_seq,
zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
sizeof(mac_buf)),
@@ -2422,14 +2374,12 @@ int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn,
int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac,
bool clear_static)
{
- char buf[ETHER_ADDR_STRLEN];
bool old_bgp_ready;
bool new_bgp_ready;
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("DEL MAC %s VNI %u seq %u flags 0x%x nbr count %u",
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- zevpn->vni, mac->loc_seq, mac->flags,
+ zlog_debug("DEL MAC %pEA VNI %u seq %u flags 0x%x nbr count %u",
+ &mac->macaddr, zevpn->vni, mac->loc_seq, mac->flags,
listcount(mac->neigh_list));
old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
@@ -2443,9 +2393,8 @@ int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, zebra_mac_t *mac,
char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "re-add sync-mac vni %u mac %s es %s seq %d f %s",
- zevpn->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "re-add sync-mac vni %u mac %pEA es %s seq %d f %s",
+ zevpn->vni, &mac->macaddr,
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
zebra_evpn_zebra_mac_flag_dump(
mac, mac_buf, sizeof(mac_buf)));
@@ -2503,7 +2452,6 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
struct ethaddr *macaddr, vlanid_t vlan_id,
bool def_gw)
{
- char buf[ETHER_ADDR_STRLEN];
zebra_mac_t *mac;
ns_id_t local_ns_id = NS_DEFAULT;
struct zebra_vrf *zvrf;
@@ -2517,9 +2465,8 @@ int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
mac = zebra_evpn_mac_add(zevpn, macaddr);
if (!mac) {
flog_err(EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add MAC %s intf %s(%u) VID %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vlan_id);
+ "Failed to add MAC %pEA intf %s(%u) VID %u",
+ macaddr, ifp->name, ifp->ifindex, vlan_id);
return -1;
}
}
diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c
index dea0fea975..0e31617c4f 100644
--- a/zebra/zebra_evpn_neigh.c
+++ b/zebra/zebra_evpn_neigh.c
@@ -209,8 +209,6 @@ static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n,
zebra_mac_t *mac,
bool send_mac_update)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
bool old_static;
bool new_static;
@@ -228,11 +226,8 @@ static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n,
new_static = zebra_evpn_mac_is_static(mac);
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "sync-neigh ref mac vni %u ip %s mac %s ref %d",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf,
- sizeof(macbuf)),
+ "sync-neigh ref mac vni %u ip %pIA mac %pEA ref %d",
+ n->zevpn->vni, &n->ip, &n->emac,
mac->sync_neigh_cnt);
if ((old_static != new_static) && send_mac_update)
/* program the local mac in the kernel */
@@ -248,8 +243,6 @@ static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n,
bool force_clear_static,
const char *caller)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
struct zebra_ns *zns;
struct interface *ifp;
bool set_static;
@@ -260,11 +253,8 @@ static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n,
if (!ifp) {
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped",
- caller, n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf,
- sizeof(macbuf)),
+ "%s: dp-install sync-neigh vni %u ip %pIA mac %pEA if %d f 0x%x skipped",
+ caller, n->zevpn->vni, &n->ip, &n->emac,
n->ifindex, n->flags);
return;
}
@@ -282,10 +272,8 @@ static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n,
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s",
- caller, n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ "%s: dp-install sync-neigh vni %u ip %pIA mac %pEA if %s(%d) f 0x%x%s%s%s",
+ caller, n->zevpn->vni, &n->ip, &n->emac,
ifp->name, n->ifindex, n->flags,
set_router ? " router" : "",
set_static ? " static" : "",
@@ -375,8 +363,6 @@ void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static,
zebra_mac_t *mac = n->mac;
bool old_mac_static;
bool new_mac_static;
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
if (old_n_static == new_n_static)
return;
@@ -411,10 +397,9 @@ void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static,
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s",
- n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
- n->flags, mac->sync_neigh_cnt,
+ "sync-neigh ref-chg vni %u ip %pIA mac %pEA f 0x%x %d%s%s%s%s by %s",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags,
+ mac->sync_neigh_cnt,
old_n_static ? " old_n_static" : "",
new_n_static ? " new_n_static" : "",
old_mac_static ? " old_mac_static" : "",
@@ -434,8 +419,6 @@ static int zebra_evpn_neigh_hold_exp_cb(struct thread *t)
bool new_bgp_ready;
bool old_n_static;
bool new_n_static;
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
n = THREAD_ARG(t);
/* the purpose of the hold timer is to age out the peer-active
@@ -451,11 +434,8 @@ static int zebra_evpn_neigh_hold_exp_cb(struct thread *t)
new_n_static = zebra_evpn_neigh_is_static(n);
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
- n->flags);
+ zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold expired",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags);
/* re-program the local neigh in the dataplane if the neigh is no
* longer static
@@ -475,18 +455,12 @@ static int zebra_evpn_neigh_hold_exp_cb(struct thread *t)
static inline void zebra_evpn_neigh_start_hold_timer(zebra_neigh_t *n)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
-
if (n->hold_timer)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
- n->flags);
+ zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold start",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags);
thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n,
zmh_info->neigh_hold_time, &n->hold_timer);
}
@@ -496,8 +470,6 @@ static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n,
{
zebra_mac_t *mac = n->mac;
zebra_evpn_t *zevpn = n->zevpn;
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
bool old_static;
bool new_static;
@@ -511,11 +483,8 @@ static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n,
new_static = zebra_evpn_mac_is_static(mac);
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "sync-neigh deref mac vni %u ip %s mac %s ref %d",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf,
- sizeof(macbuf)),
+ "sync-neigh deref mac vni %u ip %pIA mac %pEA ref %d",
+ n->zevpn->vni, &n->ip, &n->emac,
mac->sync_neigh_cnt);
if ((old_static != new_static) && send_mac_update)
/* program the local mac in the kernel */
@@ -532,8 +501,6 @@ bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
struct ethaddr *macaddr, uint32_t seq,
bool sync)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
uint32_t tmp_seq;
const char *n_type;
@@ -555,24 +522,18 @@ bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n,
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH
|| IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s-macip accept vni %u %s mac %s IP %s lower seq %u f 0x%x",
+ "%s-macip accept vni %u %s mac %pEA IP %pIA lower seq %u f 0x%x",
sync ? "sync" : "remote", zevpn->vni,
- n_type,
- prefix_mac2str(macaddr, macbuf,
- sizeof(macbuf)),
- ipaddr2str(&n->ip, ipbuf,
- sizeof(ipbuf)),
+ n_type, macaddr, &n->ip,
tmp_seq, n->flags);
return true;
}
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s-macip ignore vni %u %s mac %s IP %s as existing has higher seq %u f 0x%x",
+ "%s-macip ignore vni %u %s mac %pEA IP %pIA as existing has higher seq %u f 0x%x",
sync ? "sync" : "remote", zevpn->vni, n_type,
- prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- tmp_seq, n->flags);
+ macaddr, &n->ip, tmp_seq, n->flags);
return false;
}
@@ -636,15 +597,10 @@ void zebra_evpn_sync_neigh_del(zebra_neigh_t *n)
{
bool old_n_static;
bool new_n_static;
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
- n->flags);
+ zlog_debug("sync-neigh del vni %u ip %pIA mac %pEA f 0x%x",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags);
old_n_static = zebra_evpn_neigh_is_static(n);
UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY);
@@ -676,8 +632,6 @@ zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
bool old_mac_static;
bool new_mac_static;
bool set_dp_inactive = false;
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
bool created;
ifindex_t ifindex = 0;
@@ -778,11 +732,8 @@ zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags))
zlog_debug(
- "sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf,
- sizeof(macbuf)),
+ "sync-neigh vni %u ip %pIA mac %pEA old_f 0x%x new_f 0x%x",
+ n->zevpn->vni, &n->ip, &n->emac,
old_flags, n->flags);
new_n_static = zebra_evpn_neigh_is_static(n);
@@ -844,10 +795,9 @@ zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n,
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s",
+ "sync-neigh %s vni %u ip %pIA mac %pEA if %s(%d) seq %d f 0x%x%s%s",
created ? "created" : "updated", n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
+ &n->ip, &n->emac,
ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags,
inform_bgp ? " inform_bgp" : "",
inform_dataplane ? " inform_dp" : "");
@@ -970,14 +920,13 @@ void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn,
zebra_neigh_t *n = NULL;
struct listnode *node = NULL;
struct zebra_vrf *zvrf = NULL;
- char buf[ETHER_ADDR_STRLEN];
zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id);
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on local MAC %s %s, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- seq_change ? "CHANGE" : "ADD", zevpn->vni);
+ zlog_debug("Processing neighbors on local MAC %pEA %s, VNI %u",
+ &zmac->macaddr, seq_change ? "CHANGE" : "ADD",
+ zevpn->vni);
/* Walk all neighbors and mark any inactive local neighbors as
* active and/or update sequence number upon a move, and inform BGP.
@@ -1012,12 +961,10 @@ void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn,
{
zebra_neigh_t *n = NULL;
struct listnode *node = NULL;
- char buf[ETHER_ADDR_STRLEN];
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- zevpn->vni);
+ zlog_debug("Processing neighbors on local MAC %pEA DEL, VNI %u",
+ &zmac->macaddr, zevpn->vni);
/* Walk all local neighbors and mark as inactive and inform
* BGP, if needed.
@@ -1047,12 +994,10 @@ void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn,
{
zebra_neigh_t *n = NULL;
struct listnode *node = NULL;
- char buf[ETHER_ADDR_STRLEN];
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u",
- prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)),
- zevpn->vni);
+ zlog_debug("Processing neighbors on remote MAC %pEA ADD, VNI %u",
+ &zmac->macaddr, zevpn->vni);
/* Walk all local neighbors and mark as inactive and inform
* BGP, if needed.
@@ -1085,15 +1030,11 @@ static inline void zebra_evpn_local_neigh_update_log(
bool old_bgp_ready, bool new_bgp_ready, bool inform_dataplane,
bool inform_bgp, const char *sfx)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
-
if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
return;
- zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", pfx,
- n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), n->flags,
+ zlog_debug("%s neigh vni %u ip %pIA mac %pEA f 0x%x%s%s%s%s%s%s %s", pfx,
+ n->zevpn->vni, &n->ip, &n->emac, n->flags,
is_router ? " router" : "",
local_inactive ? " local-inactive" : "",
old_bgp_ready ? " old_bgp_ready" : "",
@@ -1154,8 +1095,6 @@ static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t)
struct zebra_vrf *zvrf = NULL;
zebra_neigh_t *nbr = NULL;
zebra_evpn_t *zevpn = NULL;
- char buf1[INET6_ADDRSTRLEN];
- char buf2[ETHER_ADDR_STRLEN];
nbr = THREAD_ARG(t);
@@ -1174,10 +1113,8 @@ static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired",
- __func__,
- prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags,
+ "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x learn count %u vni %u auto recovery expired",
+ __func__, &nbr->emac, &nbr->ip, nbr->flags,
nbr->dad_count, zevpn->vni);
UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
@@ -1207,8 +1144,6 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
{
struct timeval elapsed = {0, 0};
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
bool reset_params = false;
if (!zebra_evpn_do_dup_addr_detect(zvrf))
@@ -1221,10 +1156,8 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u",
- __func__,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x skip installing, learn count %u recover time %u",
+ __func__, &nbr->emac, &nbr->ip,
nbr->flags, nbr->dad_count,
zvrf->dad_freeze_time);
@@ -1261,10 +1194,8 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
if (reset_params) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u",
- __func__,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x detection time passed, reset learn count %u",
+ __func__, &nbr->emac, &nbr->ip,
nbr->flags, nbr->dad_count);
/* Reset learn count but do not start detection
* during REMOTE learn event.
@@ -1294,10 +1225,8 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
if (nbr->dad_count >= zvrf->dad_max_moves) {
flog_warn(
EC_ZEBRA_DUP_IP_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %pI4",
- nbr->zevpn->vni,
- prefix_mac2str(&nbr->emac, buf, sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1, sizeof(buf1)),
+ "VNI %u: MAC %pEA IP %pIA detected as duplicate during %s VTEP %pI4",
+ nbr->zevpn->vni, &nbr->emac, &nbr->ip,
is_local ? "local update, last" : "remote update, from",
&vtep_ip);
@@ -1311,12 +1240,8 @@ zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr,
if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start",
- __func__,
- prefix_mac2str(&nbr->emac, buf,
- sizeof(buf)),
- ipaddr2str(&nbr->ip, buf1,
- sizeof(buf1)),
+ "%s: duplicate addr MAC %pEA IP %pIA flags 0x%x auto recovery time %u start",
+ __func__, &nbr->emac, &nbr->ip,
nbr->flags, zvrf->dad_freeze_time);
thread_add_timer(zrouter.master,
@@ -1334,8 +1259,6 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
bool is_router, bool local_inactive,
bool dp_static)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
struct zebra_vrf *zvrf;
zebra_neigh_t *n = NULL;
zebra_mac_t *zmac = NULL, *old_zmac = NULL;
@@ -1357,15 +1280,12 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
if (!zmac) {
/* create a dummy MAC if the MAC is not already present */
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("AUTO MAC %s created for neigh %s on VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)),
- zevpn->vni);
+ zlog_debug("AUTO MAC %pEA created for neigh %pIA on VNI %u",
+ macaddr, ip, zevpn->vni);
zmac = zebra_evpn_mac_add(zevpn, macaddr);
if (!zmac) {
- zlog_debug("Failed to add MAC %s VNI %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zlog_debug("Failed to add MAC %pEA VNI %u", macaddr,
zevpn->vni);
return -1;
}
@@ -1402,10 +1322,9 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
if (!n) {
flog_err(
EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
- ipaddr2str(ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, zevpn->vni);
+ "Failed to add neighbor %pIA MAC %pEA intf %s(%u) -> VNI %u",
+ ip, macaddr, ifp->name, ifp->ifindex,
+ zevpn->vni);
return -1;
}
/* Set "local" forwarding info. */
@@ -1606,9 +1525,8 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) {
flog_warn(
EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC",
- zevpn->vni, prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ "VNI %u: MAC %pEA IP %pIA detected as duplicate during local update, inherit duplicate from MAC",
+ zevpn->vni, macaddr, &n->ip);
}
/* For IP Duplicate Address Detection (DAD) is trigger,
@@ -1651,9 +1569,9 @@ int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
if (upd_mac_seq && zmac->loc_seq != mac_new_seq) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Seq changed for MAC %s VNI %u - old %u new %u",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- zevpn->vni, zmac->loc_seq, mac_new_seq);
+ "Seq changed for MAC %pEA VNI %u - old %u new %u",
+ macaddr, zevpn->vni,
+ zmac->loc_seq, mac_new_seq);
zmac->loc_seq = mac_new_seq;
if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr,
zmac->flags,
@@ -1682,8 +1600,6 @@ int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
struct ipaddr *ip, struct ethaddr *macaddr,
uint16_t state)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
zebra_neigh_t *n = NULL;
zebra_mac_t *zmac = NULL;
@@ -1708,10 +1624,8 @@ int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp,
zmac = zebra_evpn_mac_lookup(zevpn, macaddr);
if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) {
zlog_debug(
- "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local",
- ipaddr2str(&n->ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- zevpn->vni);
+ "Ignore remote neigh %pIA (MAC %pEA) on L2-VNI %u - MAC unknown or local",
+ &n->ip, macaddr, zevpn->vni);
return -1;
}
@@ -2140,8 +2054,6 @@ void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
{
zebra_neigh_t *n;
int update_neigh = 0;
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
zebra_mac_t *old_mac = NULL;
bool old_static = false;
bool do_dad = false;
@@ -2167,11 +2079,9 @@ void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
mac, 0);
if (!n) {
zlog_warn(
- "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %pI4",
- ipaddr2str(ipaddr, buf1, sizeof(buf1)),
- prefix_mac2str(&mac->macaddr, buf,
- sizeof(buf)),
- zevpn->vni, &vtep_ip);
+ "Failed to add Neigh %pIA MAC %pEA VNI %u Remote VTEP %pI4",
+ ipaddr, &mac->macaddr, zevpn->vni,
+ &vtep_ip);
return;
}
@@ -2192,12 +2102,8 @@ void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
old_static = zebra_evpn_neigh_is_static(n);
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "sync->remote neigh vni %u ip %s mac %s seq %d f0x%x",
- n->zevpn->vni,
- ipaddr2str(&n->ip, buf1,
- sizeof(buf1)),
- prefix_mac2str(&n->emac, buf,
- sizeof(buf)),
+ "sync->remote neigh vni %u ip %pIA mac %pEA seq %d f0x%x",
+ n->zevpn->vni, &n->ip, &n->emac,
seq, n->flags);
zebra_evpn_neigh_clear_sync_info(n);
if (IS_ZEBRA_NEIGH_ACTIVE(n))
@@ -2258,10 +2164,8 @@ void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf,
if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_mac, mac, n)) {
flog_warn(
EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
- "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC",
- zevpn->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- ipaddr2str(&n->ip, buf1, sizeof(buf1)));
+ "VNI %u: MAC %pEA IP %pIA detected as duplicate during remote update, inherit duplicate from MAC",
+ zevpn->vni, &mac->macaddr, &n->ip);
}
/* Check duplicate address detection for IP */
@@ -2280,8 +2184,6 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
struct ipaddr *ip, zebra_mac_t *mac)
{
zebra_neigh_t *n;
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
assert(mac);
@@ -2291,9 +2193,8 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
if (!n) {
flog_err(
EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
- ipaddr2str(ip, buf2, sizeof(buf2)),
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
+ "Failed to add neighbor %pIA MAC %pEA intf %s(%u) -> VNI %u",
+ ip, &mac->macaddr,
ifp->name, ifp->ifindex, zevpn->vni);
return -1;
}
@@ -2316,10 +2217,9 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %pEA IP %pIA add to BGP with flags 0x%x",
ifp->name, ifp->ifindex, zevpn->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+ &mac->macaddr, ip, n->flags);
zebra_evpn_neigh_send_add_to_client(
zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq);
@@ -2328,10 +2228,9 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn,
SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x",
+ "SVI %s(%u) L2-VNI %u, sending SVI MAC %pEA IP %pIA add to BGP with flags 0x%x",
ifp->name, ifp->ifindex, zevpn->vni,
- prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+ &mac->macaddr, ip, n->flags);
zebra_evpn_neigh_send_add_to_client(
zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq);
@@ -2344,8 +2243,6 @@ void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
struct zebra_vrf *zvrf, zebra_neigh_t *n,
zebra_mac_t *mac, struct ipaddr *ipaddr)
{
- char buf1[INET6_ADDRSTRLEN];
-
if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)
&& CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)
&& (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) {
@@ -2354,10 +2251,9 @@ void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn,
vlan_if = zevpn_map_to_svi(zevpn);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
- __func__,
- ipaddr2str(ipaddr, buf1, sizeof(buf1)),
- n->flags, vlan_if ? vlan_if->name : "Unknown");
+ "%s: IP %pIA (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry",
+ __func__, ipaddr, n->flags,
+ vlan_if ? vlan_if->name : "Unknown");
if (vlan_if)
neigh_read_specific_ip(ipaddr, vlan_if);
}
@@ -2384,8 +2280,6 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
zebra_mac_t *zmac;
bool old_bgp_ready;
bool new_bgp_ready;
- char buf[INET6_ADDRSTRLEN];
- char buf2[ETHER_ADDR_STRLEN];
struct zebra_vrf *zvrf;
/* If entry doesn't exist, nothing to do. */
@@ -2397,9 +2291,8 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
if (!zmac) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Trying to del a neigh %s without a mac %s on VNI %u",
- ipaddr2str(ip, buf, sizeof(buf)),
- prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ "Trying to del a neigh %pIA without a mac %pEA on VNI %u",
+ ip, &n->emac,
zevpn->vni);
return 0;
@@ -2419,10 +2312,8 @@ int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip)
old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n);
if (zebra_evpn_neigh_is_static(n)) {
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x",
- n->zevpn->vni,
- ipaddr2str(&n->ip, buf, sizeof(buf)),
- prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ zlog_debug("re-add sync neigh vni %u ip %pIA mac %pEA 0x%x",
+ n->zevpn->vni, &n->ip, &n->emac,
n->flags);
if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE))
diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h
index eac17a09b4..05156c1255 100644
--- a/zebra/zebra_evpn_neigh.h
+++ b/zebra/zebra_evpn_neigh.h
@@ -167,18 +167,12 @@ static inline bool zebra_evpn_neigh_is_ready_for_bgp(zebra_neigh_t *n)
static inline void zebra_evpn_neigh_stop_hold_timer(zebra_neigh_t *n)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
-
if (!n->hold_timer)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)),
- n->flags);
+ zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold stop",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags);
THREAD_OFF(n->hold_timer);
}
@@ -188,19 +182,13 @@ void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static,
static inline bool zebra_evpn_neigh_clear_sync_info(zebra_neigh_t *n)
{
- char macbuf[ETHER_ADDR_STRLEN];
- char ipbuf[INET6_ADDRSTRLEN];
bool old_n_static = false;
bool new_n_static = false;
if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) {
if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
- zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear",
- n->zevpn->vni,
- ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
- prefix_mac2str(&n->emac, macbuf,
- sizeof(macbuf)),
- n->flags);
+ zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x clear",
+ n->zevpn->vni, &n->ip, &n->emac, n->flags);
old_n_static = zebra_evpn_neigh_is_static(n);
UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS);
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
index 73534c4332..099ac1434b 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1555,7 +1555,6 @@ static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac)
static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason)
{
- char buf[ETHER_ADDR_STRLEN];
struct fpm_mac_info_t *fpm_mac, key;
struct interface *vxlan_if, *svi_if;
bool mac_found = false;
@@ -1568,9 +1567,8 @@ static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
return 0;
if (reason) {
- zfpm_debug("triggering update to FPM - Reason: %s - %s",
- reason,
- prefix_mac2str(&rmac->macaddr, buf, sizeof(buf)));
+ zfpm_debug("triggering update to FPM - Reason: %s - %pEA",
+ reason, &rmac->macaddr);
}
vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index f7c5da5dec..cebd576365 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -500,9 +500,8 @@ static void zfpm_log_route_info(struct netlink_route_info *ri,
unsigned int i;
char buf[PREFIX_STRLEN];
- zfpm_debug("%s : %s %s, Proto: %s, Metric: %u", label,
- nl_msg_type_to_str(ri->nlmsg_type),
- prefix2str(ri->prefix, buf, sizeof(buf)),
+ zfpm_debug("%s : %s %pFX, Proto: %s, Metric: %u", label,
+ nl_msg_type_to_str(ri->nlmsg_type), ri->prefix,
nl_rtproto_to_str(ri->rtm_protocol),
ri->metric ? *ri->metric : 0);
@@ -557,7 +556,6 @@ int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re,
int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
size_t in_buf_len)
{
- char buf1[ETHER_ADDR_STRLEN];
size_t buf_offset;
struct macmsg {
@@ -600,11 +598,10 @@ int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
assert(req->hdr.nlmsg_len < in_buf_len);
- zfpm_debug("Tx %s family %s ifindex %u MAC %s DEST %pI4",
+ zfpm_debug("Tx %s family %s ifindex %u MAC %pEA DEST %pI4",
nl_msg_type_to_str(req->hdr.nlmsg_type),
nl_family_to_str(req->ndm.ndm_family), req->ndm.ndm_ifindex,
- prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)),
- &mac->r_vtep_ip);
+ &mac->macaddr, &mac->r_vtep_ip);
return req->hdr.nlmsg_len;
}
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 3f567d9a70..f3ccf83fbc 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -371,8 +371,8 @@ void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi,
*/
if (nh && (nh->next == NULL)) {
switch (nh->type) {
- case (NEXTHOP_TYPE_IFINDEX):
- case (NEXTHOP_TYPE_BLACKHOLE):
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
/*
* This switch case handles setting the afi different
* for ipv4/v6 routes. Ifindex/blackhole nexthop
@@ -383,12 +383,12 @@ void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi,
*/
nhe->afi = afi;
break;
- case (NEXTHOP_TYPE_IPV4_IFINDEX):
- case (NEXTHOP_TYPE_IPV4):
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
nhe->afi = AFI_IP;
break;
- case (NEXTHOP_TYPE_IPV6_IFINDEX):
- case (NEXTHOP_TYPE_IPV6):
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
nhe->afi = AFI_IP6;
break;
}
@@ -1789,8 +1789,9 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop,
* if at all possible. Set the nexthop->ifindex and resolved_id
* as appropriate
*/
-static int nexthop_active(afi_t afi, struct route_entry *re,
- struct nexthop *nexthop, struct route_node *top)
+static int nexthop_active(afi_t afi, struct nexthop *nexthop,
+ const struct prefix *top, int type, uint32_t flags,
+ uint32_t *pmtu)
{
struct prefix p;
struct route_table *table;
@@ -1805,33 +1806,58 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
struct in_addr local_ipv4;
struct in_addr *ipv4;
+ /* Reset some nexthop attributes that we'll recompute if necessary */
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
- || nexthop->type == NEXTHOP_TYPE_IPV6)
+ || (nexthop->type == NEXTHOP_TYPE_IPV6))
nexthop->ifindex = 0;
-
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
nexthops_free(nexthop->resolved);
nexthop->resolved = NULL;
- re->nexthop_mtu = 0;
-
- if (IS_ZEBRA_DEBUG_NHG_DETAIL)
- zlog_debug("%s: re %p, nexthop %pNHv",
- __func__, re, nexthop);
/*
- * If the kernel has sent us a NEW route, then
- * by golly gee whiz it's a good route.
- *
- * If its an already INSTALLED route we have already handled, then the
- * kernel route's nexthop might have became unreachable
- * and we have to handle that.
+ * Some nexthop types get special handling, possibly skipping
+ * the normal processing.
*/
- if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
- && (re->type == ZEBRA_ROUTE_KERNEL
- || re->type == ZEBRA_ROUTE_SYSTEM))
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
+ /*
+ * If the interface exists and its operative or its a kernel
+ * route and interface is up, its active. We trust kernel routes
+ * to be good.
+ */
+ if (ifp
+ && (if_is_operative(ifp)
+ || (if_is_up(ifp)
+ && (type == ZEBRA_ROUTE_KERNEL
+ || type == ZEBRA_ROUTE_SYSTEM))))
+ return 1;
+ else
+ return 0;
+ break;
+
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
+ if (ifp && if_is_operative(ifp))
+ return 1;
+ else
+ return 0;
+ }
+ break;
+
+ case NEXTHOP_TYPE_BLACKHOLE:
return 1;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ default:
+ break;
+ }
+
/*
* If the nexthop has been marked as 'onlink' we just need to make
* sure the nexthop's interface is known and is operational.
@@ -1853,10 +1879,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
return 1;
}
- if ((top->p.family == AF_INET && top->p.prefixlen == 32
- && nexthop->gate.ipv4.s_addr == top->p.u.prefix4.s_addr)
- || (top->p.family == AF_INET6 && top->p.prefixlen == 128
- && memcmp(&nexthop->gate.ipv6, &top->p.u.prefix6, 16) == 0)) {
+ if (top &&
+ ((top->family == AF_INET && top->prefixlen == 32
+ && nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr)
+ || (top->family == AF_INET6 && top->prefixlen == 128
+ && memcmp(&nexthop->gate.ipv6, &top->u.prefix6, 16) == 0))) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
" :%s: Attempting to install a max prefixlength route through itself",
@@ -1873,6 +1900,9 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
ipv4 = &nexthop->gate.ipv4;
}
+ /* Processing for nexthops with SR 'color' attribute, using
+ * the corresponding SR policy object.
+ */
if (nexthop->srte_color) {
struct ipaddr endpoint = {0};
struct zebra_sr_policy *policy;
@@ -1950,7 +1980,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
* resolved by a route NH1. The exception is if the route is a
* host route.
*/
- if (rn == top)
+ if (prefix_same(&rn->p, top))
if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
|| ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
@@ -2020,7 +2050,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
match->nhe->id, newhop);
return 1;
- } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
+ } else if (CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
struct nexthop_group *nhg;
resolved = 0;
@@ -2079,10 +2109,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
NULL);
resolved = 1;
}
+
done_with_match:
- if (resolved)
- re->nexthop_mtu = match->mtu;
- else if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ /* Capture resolving mtu */
+ if (resolved) {
+ if (pmtu)
+ *pmtu = match->mtu;
+
+ } else if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
" %s: Recursion failed to find",
__func__);
@@ -2092,9 +2126,9 @@ done_with_match:
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
zlog_debug(
" %s: Route Type %s has not turned on recursion",
- __func__, zebra_route_string(re->type));
- if (re->type == ZEBRA_ROUTE_BGP
- && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
+ __func__, zebra_route_string(type));
+ if (type == ZEBRA_ROUTE_BGP
+ && !CHECK_FLAG(flags, ZEBRA_FLAG_IBGP))
zlog_debug(
" EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
}
@@ -2113,20 +2147,17 @@ done_with_match:
* appropriately as well. An existing route map can turn an
* otherwise active nexthop into inactive, but not vice versa.
*
- * If it finds a nexthop recursively, set the resolved_id
- * to match that nexthop's nhg_hash_entry ID;
- *
* The return value is the final value of 'ACTIVE' flag.
*/
static unsigned nexthop_active_check(struct route_node *rn,
struct route_entry *re,
struct nexthop *nexthop)
{
- struct interface *ifp;
route_map_result_t ret = RMAP_PERMITMATCH;
afi_t family;
const struct prefix *p, *src_p;
struct zebra_vrf *zvrf;
+ uint32_t mtu = 0;
srcdest_rnode_prefixes(rn, &p, &src_p);
@@ -2137,19 +2168,28 @@ static unsigned nexthop_active_check(struct route_node *rn,
else
family = 0;
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug("%s: re %p, nexthop %pNHv", __func__, re, nexthop);
+
+ /*
+ * If the kernel has sent us a NEW route, then
+ * by golly gee whiz it's a good route.
+ *
+ * If its an already INSTALLED route we have already handled, then the
+ * kernel route's nexthop might have became unreachable
+ * and we have to handle that.
+ */
+ if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) &&
+ (re->type == ZEBRA_ROUTE_KERNEL ||
+ re->type == ZEBRA_ROUTE_SYSTEM)) {
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ goto skip_check;
+ }
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
- ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
- /*
- * If the interface exists and its operative or its a kernel
- * route and interface is up, its active. We trust kernel routes
- * to be good.
- */
- if (ifp
- && (if_is_operative(ifp)
- || (if_is_up(ifp)
- && (re->type == ZEBRA_ROUTE_KERNEL
- || re->type == ZEBRA_ROUTE_SYSTEM))))
+ if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type,
+ re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -2157,14 +2197,16 @@ static unsigned nexthop_active_check(struct route_node *rn,
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
- if (nexthop_active(AFI_IP, re, nexthop, rn))
+ if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type,
+ re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
case NEXTHOP_TYPE_IPV6:
family = AFI_IP6;
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type,
+ re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -2173,19 +2215,12 @@ static unsigned nexthop_active_check(struct route_node *rn,
/* RFC 5549, v4 prefix with v6 NH */
if (rn->p.family != AF_INET)
family = AFI_IP6;
- if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
- ifp = if_lookup_by_index(nexthop->ifindex,
- nexthop->vrf_id);
- if (ifp && if_is_operative(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- } else {
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
+
+ if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type,
+ re->flags, &mtu))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
case NEXTHOP_TYPE_BLACKHOLE:
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -2194,6 +2229,8 @@ static unsigned nexthop_active_check(struct route_node *rn,
break;
}
+skip_check:
+
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(" %s: Unable to find active nexthop",
@@ -2201,6 +2238,18 @@ static unsigned nexthop_active_check(struct route_node *rn,
return 0;
}
+ /* Capture recursive nexthop mtu.
+ * TODO -- the code used to just reset the re's value to zero
+ * for each nexthop, and then jam any resolving route's mtu value in,
+ * whether or not that was zero, or lt/gt any existing value? The
+ * way this is used appears to be as a floor value, so let's try
+ * using it that way here.
+ */
+ if (mtu > 0) {
+ if (re->nexthop_mtu == 0 || re->nexthop_mtu > mtu)
+ re->nexthop_mtu = mtu;
+ }
+
/* XXX: What exactly do those checks do? Do we support
* e.g. IPv4 routes with IPv6 nexthops or vice versa?
*/
@@ -2214,7 +2263,7 @@ static unsigned nexthop_active_check(struct route_node *rn,
* Possibly it may be better to use only the rib_table_info
* in every case.
*/
- if (!family) {
+ if (family == 0) {
struct rib_table_info *info;
info = srcdest_rnode_table_info(rn);
@@ -2292,6 +2341,9 @@ static uint32_t nexthop_list_active_update(struct route_node *rn,
nexthop = nhg->nexthop;
+ /* Init recursive nh mtu */
+ re->nexthop_mtu = 0;
+
/* Process nexthops one-by-one */
for ( ; nexthop; nexthop = nexthop->next) {
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
index db20f2beaf..2de34fec68 100644
--- a/zebra/zebra_nhg.h
+++ b/zebra/zebra_nhg.h
@@ -180,8 +180,9 @@ struct nhg_ctx {
vrf_id_t vrf_id;
afi_t afi;
+
/*
- * This should only every be ZEBRA_ROUTE_NHG unless we get a a kernel
+ * This should only ever be ZEBRA_ROUTE_NHG unless we get a a kernel
* created nexthop not made by us.
*/
int type;
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 128edd9fd3..bc3c68638d 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -2830,8 +2830,10 @@ void _route_entry_dump(const char *func, union prefixconstptr pp,
struct vrf *vrf = vrf_lookup_by_id(re->vrf_id);
struct nexthop_group *nhg;
+ prefix2str(pp, straddr, sizeof(straddr));
+
zlog_debug("%s: dumping RE entry %p for %s%s%s vrf %s(%u)", func,
- (const void *)re, prefix2str(pp, straddr, sizeof(straddr)),
+ (const void *)re, straddr,
is_srcdst ? " from " : "",
is_srcdst ? prefix2str(src_pp, srcaddr, sizeof(srcaddr))
: "",
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 6cb8010364..e5efbe0d4a 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -1226,7 +1226,6 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
*/
static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
{
- char buf[ETHER_ADDR_STRLEN];
const struct zebra_if *zif = NULL, *br_zif;
const struct zebra_l2info_vxlan *vxl = NULL;
const struct interface *br_ifp;
@@ -1240,10 +1239,8 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
if (!zl3vni->vxlan_if) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
- prefix_mac2str(&zrmac->macaddr,
- buf, sizeof(buf)),
- zl3vni->vni, zl3vni);
+ "RMAC %pEA on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
+ &zrmac->macaddr, zl3vni->vni, zl3vni);
return -1;
}
@@ -1277,8 +1274,6 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
const struct ipaddr *vtep_ip,
const struct prefix *host_prefix)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf1[INET6_ADDRSTRLEN];
zebra_mac_t *zrmac = NULL;
zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
@@ -1288,11 +1283,8 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
zrmac = zl3vni_rmac_add(zl3vni, rmac);
if (!zrmac) {
zlog_debug(
- "Failed to add RMAC %s L3VNI %u Remote VTEP %s, prefix %pFX",
- prefix_mac2str(rmac, buf, sizeof(buf)),
- zl3vni->vni,
- ipaddr2str(vtep_ip, buf1, sizeof(buf1)),
- host_prefix);
+ "Failed to add RMAC %pEA L3VNI %u Remote VTEP %pIA, prefix %pFX",
+ rmac, zl3vni->vni, vtep_ip, host_prefix);
return -1;
}
memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
@@ -1308,12 +1300,9 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
&vtep_ip->ipaddr_v4)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "L3VNI %u Remote VTEP change(%pI4 -> %s) for RMAC %s, prefix %pFX",
- zl3vni->vni,
- &zrmac->fwd_info.r_vtep_ip,
- ipaddr2str(vtep_ip, buf1, sizeof(buf1)),
- prefix_mac2str(rmac, buf, sizeof(buf)),
- host_prefix);
+ "L3VNI %u Remote VTEP change(%pI4 -> %pIA) for RMAC %pEA, prefix %pFX",
+ zl3vni->vni, &zrmac->fwd_info.r_vtep_ip,
+ vtep_ip, rmac, host_prefix);
zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
@@ -1470,9 +1459,6 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
const struct ethaddr *rmac,
const struct prefix *host_prefix)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf1[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
zebra_neigh_t *nh = NULL;
/* Create the next hop entry, or update its mac, if necessary. */
@@ -1481,10 +1467,8 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac);
if (!nh) {
zlog_debug(
- "Failed to add NH %s as Neigh (RMAC %s L3-VNI %u prefix %pFX)",
- ipaddr2str(vtep_ip, buf1, sizeof(buf2)),
- prefix_mac2str(rmac, buf, sizeof(buf)),
- zl3vni->vni, host_prefix);
+ "Failed to add NH %pIA as Neigh (RMAC %pEA L3-VNI %u prefix %pFX)",
+ vtep_ip, rmac, zl3vni->vni, host_prefix);
return -1;
}
@@ -1493,11 +1477,8 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
} else if (memcmp(&nh->emac, rmac, ETH_ALEN) != 0) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "L3VNI %u RMAC change(%s --> %s) for nexthop %s, prefix %pFX",
- zl3vni->vni,
- prefix_mac2str(&nh->emac, buf, sizeof(buf)),
- prefix_mac2str(rmac, buf1, sizeof(buf1)),
- ipaddr2str(vtep_ip, buf2, sizeof(buf2)),
+ "L3VNI %u RMAC change(%pEA --> %pEA) for nexthop %pIA, prefix %pFX",
+ zl3vni->vni, &nh->emac, rmac, vtep_ip,
host_prefix);
memcpy(&nh->emac, rmac, ETH_ALEN);
@@ -1878,8 +1859,6 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
struct zserv *client = NULL;
struct ethaddr svi_rmac, vrr_rmac = {.octet = {0} };
struct zebra_vrf *zvrf;
- char buf[ETHER_ADDR_STRLEN];
- char buf1[ETHER_ADDR_STRLEN];
bool is_anycast_mac = true;
client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
@@ -1920,11 +1899,9 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Send L3_VNI_ADD %u VRF %s RMAC %s VRR %s local-ip %pI4 filter %s to %s",
+ "Send L3_VNI_ADD %u VRF %s RMAC %pEA VRR %pEA local-ip %pI4 filter %s to %s",
zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
- prefix_mac2str(&svi_rmac, buf, sizeof(buf)),
- prefix_mac2str(&vrr_rmac, buf1, sizeof(buf1)),
- &zl3vni->local_vtep_ip,
+ &svi_rmac, &vrr_rmac, &zl3vni->local_vtep_ip,
CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)
? "prefix-routes-only"
: "none",
@@ -2138,7 +2115,6 @@ static void zl3vni_del_nh_hash_entry(struct hash_bucket *bucket, void *ctx)
static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
struct ethaddr *rmac)
{
- char buf[ETHER_ADDR_STRLEN];
zebra_mac_t *zrmac = NULL;
zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
@@ -2146,8 +2122,8 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del remote RMAC %s L3VNI %u - readd",
- prefix_mac2str(rmac, buf, sizeof(buf)), zl3vni->vni);
+ zlog_debug("Del remote RMAC %pEA L3VNI %u - readd",
+ rmac, zl3vni->vni);
zl3vni_rmac_install(zl3vni, zrmac);
return 0;
@@ -3667,7 +3643,6 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
struct interface *link_if,
struct ipaddr *ip)
{
- char buf[INET6_ADDRSTRLEN];
zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
@@ -3685,9 +3660,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
if (!zevpn) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "%s: Del neighbor %s EVPN is not present for interface %s",
- __func__, ipaddr2str(ip, buf, sizeof(buf)),
- ifp->name);
+ "%s: Del neighbor %pIA EVPN is not present for interface %s",
+ __func__, ip, ifp->name);
return 0;
}
@@ -3699,9 +3673,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp,
}
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u",
- ipaddr2str(ip, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, zevpn->vni);
+ zlog_debug("Del neighbor %pIA intf %s(%u) -> L2-VNI %u",
+ ip, ifp->name, ifp->ifindex, zevpn->vni);
return zebra_evpn_neigh_del_ip(zevpn, ip);
}
@@ -3721,8 +3694,6 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
bool is_router,
bool local_inactive, bool dp_static)
{
- char buf[ETHER_ADDR_STRLEN];
- char buf2[INET6_ADDRSTRLEN];
zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
@@ -3742,9 +3713,8 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp,
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH)
zlog_debug(
- "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s%s%s%s-> L2-VNI %u",
- ipaddr2str(ip, buf2, sizeof(buf2)),
- prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
+ "Add/Update neighbor %pIA MAC %pEA intf %s(%u) state 0x%x %s%s%s%s-> L2-VNI %u",
+ ip, macaddr, ifp->name,
ifp->ifindex, state, is_ext ? "ext-learned " : "",
is_router ? "router " : "",
local_inactive ? "local_inactive " : "",
@@ -3822,7 +3792,6 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
struct ipaddr ip;
struct in_addr vtep_ip;
uint16_t l = 0, ipa_len;
- char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
memset(&macaddr, 0, sizeof(struct ethaddr));
@@ -3842,9 +3811,8 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS)
l += res_length;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Recv MACIP DEL VNI %u MAC %s%s%s Remote VTEP %pI4 from %s",
- vni,
- prefix_mac2str(&macaddr, buf, sizeof(buf)),
+ "Recv MACIP DEL VNI %u MAC %pEA%s%s Remote VTEP %pI4 from %s",
+ vni, &macaddr,
ipa_len ? " IP " : "",
ipa_len ?
ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
@@ -3872,7 +3840,6 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
uint16_t l = 0, ipa_len;
uint8_t flags = 0;
uint32_t seq;
- char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
esi_t esi;
char esi_buf[ESI_STR_LEN];
@@ -3903,11 +3870,10 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
else
strlcpy(esi_buf, "-", ESI_STR_LEN);
zlog_debug(
- "Recv %sMACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %pI4 ESI %s from %s",
+ "Recv %sMACIP ADD VNI %u MAC %pEA%s%s flags 0x%x seq %u VTEP %pI4 ESI %s from %s",
(flags & ZEBRA_MACIP_TYPE_SYNC_PATH) ?
"sync-" : "",
- vni,
- prefix_mac2str(&macaddr, buf, sizeof(buf)),
+ vni, &macaddr,
ipa_len ? " IP " : "",
ipa_len ?
ipaddr2str(&ip, buf1, sizeof(buf1)) : "",
@@ -3984,7 +3950,6 @@ static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
vni_t vni;
zebra_evpn_t *zevpn;
zebra_mac_t *mac;
- char buf[ETHER_ADDR_STRLEN];
zif = ifp->info;
assert(zif);
@@ -4011,9 +3976,8 @@ static int zebra_vxlan_check_del_local_mac(struct interface *ifp,
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Add/update remote MAC %s intf %s(%u) VNI %u flags 0x%x - del local",
- prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
- ifp->ifindex, vni, mac->flags);
+ "Add/update remote MAC %pEA intf %s(%u) VNI %u flags 0x%x - del local",
+ macaddr, ifp->name, ifp->ifindex, vni, mac->flags);
/* Remove MAC from BGP. */
zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags,
@@ -4175,7 +4139,6 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
{
zebra_evpn_t *zevpn;
struct zebra_vrf *zvrf;
- char buf[ETHER_ADDR_STRLEN];
assert(ifp);
@@ -4186,9 +4149,8 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
if (!zevpn) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- " Add/Update %sMAC %s intf %s(%u) VID %u, could not find EVPN",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
+ " Add/Update %sMAC %pEA intf %s(%u) VID %u, could not find EVPN",
+ sticky ? "sticky " : "", macaddr,
ifp->name, ifp->ifindex, vid);
return 0;
}