1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * This file is part of the PCEPlib, a PCEP protocol library.
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
7 * Author : Brady Johnson <brady@voltanet.io>
13 * Sample PCC implementation
18 #include <netdb.h> // gethostbyname
19 #include <netinet/tcp.h>
28 #include "pcep_pcc_api.h"
29 #include "pcep_utils_double_linked_list.h"
30 #include "pcep_utils_logging.h"
31 #include "pcep_utils_memory.h"
34 * PCEP PCC design spec:
35 * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
37 #define MAX_SRC_IP_STR 40
38 #define MAX_DST_IP_STR 40
39 struct cmd_line_args
{
40 char src_ip_str
[MAX_SRC_IP_STR
];
41 char dest_ip_str
[MAX_DST_IP_STR
];
44 char tcp_md5_str
[PCEP_MD5SIG_MAXKEYLEN
]; /* RFC 2385 */
46 bool eventpoll
; /* poll for pcep_event's, or use callback (default) */
49 bool pcc_active_
= true;
50 pcep_session
*session
= NULL
;
51 struct cmd_line_args
*cmd_line_args
= NULL
;
52 /* pcep_event callback variables */
53 bool pcep_event_condition
= false;
54 struct pcep_event
*event
= NULL
;
55 pthread_mutex_t pcep_event_mutex
;
56 pthread_cond_t pcep_event_cond_var
;
58 static const char DEFAULT_DEST_HOSTNAME
[] = "localhost";
59 static const char DEFAULT_DEST_HOSTNAME_IPV6
[] = "ip6-localhost";
60 static const short DEFAULT_SRC_TCP_PORT
= 4999;
63 struct cmd_line_args
*get_cmdline_args(int argc
, char *argv
[]);
64 void handle_signal_action(int sig_number
);
65 void send_pce_path_request_message(pcep_session
*session
);
66 void send_pce_report_message(pcep_session
*session
);
67 void print_queue_event(struct pcep_event
*event
);
68 void pcep_event_callback(void *cb_data
, pcep_event
*e
);
70 struct cmd_line_args
*get_cmdline_args(int argc
, char *argv
[])
72 /* Allocate and set default values */
73 struct cmd_line_args
*cmd_line_args
=
74 malloc(sizeof(struct cmd_line_args
));
75 memset(cmd_line_args
, 0, sizeof(struct cmd_line_args
));
76 strlcpy(cmd_line_args
->dest_ip_str
, DEFAULT_DEST_HOSTNAME
,
78 cmd_line_args
->src_tcp_port
= DEFAULT_SRC_TCP_PORT
;
79 cmd_line_args
->is_ipv6
= false;
81 /* Parse the cmd_line args:
90 for (; i
< argc
; ++i
) {
91 if (strcmp(argv
[i
], "-help") == 0
92 || strcmp(argv
[i
], "--help") == 0
93 || strcmp(argv
[i
], "-h") == 0) {
96 "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
100 } else if (strcmp(argv
[i
], "-ipv6") == 0) {
101 cmd_line_args
->is_ipv6
= true;
103 strlcpy(cmd_line_args
->dest_ip_str
,
104 DEFAULT_DEST_HOSTNAME_IPV6
,
107 } else if (strcmp(argv
[i
], "-eventpoll") == 0) {
108 cmd_line_args
->eventpoll
= true;
109 } else if (strcmp(argv
[i
], "-srcip") == 0) {
111 strlcpy(cmd_line_args
->src_ip_str
, argv
[++i
],
116 "%s: Invalid number of cmd_line_args for \"-srcip\"",
121 } else if (strcmp(argv
[i
], "-destip") == 0) {
123 strlcpy(cmd_line_args
->dest_ip_str
, argv
[++i
],
128 "%s: Invalid number of cmd_line_args for \"-destip\"",
133 } else if (strcmp(argv
[i
], "-srcport") == 0) {
135 cmd_line_args
->src_tcp_port
= atoi(argv
[++i
]);
139 "%s: Invalid number of cmd_line_args for \"-srcport\"",
144 } else if (strcmp(argv
[i
], "-destport") == 0) {
146 cmd_line_args
->dest_tcp_port
= atoi(argv
[++i
]);
150 "%s: Invalid number of cmd_line_args for \"-destport\"",
155 } else if (strcmp(argv
[i
], "-tcpmd5") == 0) {
157 strlcpy(cmd_line_args
->tcp_md5_str
, argv
[++i
],
158 sizeof(cmd_line_args
->tcp_md5_str
));
162 "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
168 pcep_log(LOG_ERR
, "%s: Invalid cmd_line_arg[%d] = %s",
169 __func__
, i
, argv
[i
]);
175 return cmd_line_args
;
178 void handle_signal_action(int sig_number
)
180 if (sig_number
== SIGINT
) {
181 pcep_log(LOG_INFO
, "%s: SIGINT was caught!", __func__
);
183 if (cmd_line_args
->eventpoll
== false) {
184 pthread_mutex_lock(&pcep_event_mutex
);
185 pcep_event_condition
= true;
186 pthread_cond_signal(&pcep_event_cond_var
);
187 pthread_mutex_unlock(&pcep_event_mutex
);
189 } else if (sig_number
== SIGUSR1
) {
190 pcep_log(LOG_INFO
, "%s: SIGUSR1 was caught, dumping counters",
192 dump_pcep_session_counters(session
);
193 pceplib_memory_dump();
194 } else if (sig_number
== SIGUSR2
) {
195 pcep_log(LOG_INFO
, "%s: SIGUSR2 was caught, reseting counters",
197 reset_pcep_session_counters(session
);
201 static int setup_signals(void)
204 memset(&sa
, 0, sizeof(sa
));
205 sa
.sa_handler
= handle_signal_action
;
206 if (sigaction(SIGINT
, &sa
, 0) != 0) {
207 perror("sigaction()");
211 if (sigaction(SIGUSR1
, &sa
, 0) != 0) {
212 perror("sigaction()");
216 if (sigaction(SIGUSR2
, &sa
, 0) != 0) {
217 perror("sigaction()");
224 void send_pce_path_request_message(pcep_session
*session
)
226 struct in_addr src_ipv4
;
227 struct in_addr dst_ipv4
;
228 inet_pton(AF_INET
, "1.2.3.4", &src_ipv4
);
229 inet_pton(AF_INET
, "10.20.30.40", &dst_ipv4
);
231 struct pcep_object_rp
*rp_object
=
232 pcep_obj_create_rp(1, false, false, false, false, 42, NULL
);
233 struct pcep_object_endpoints_ipv4
*ep_object
=
234 pcep_obj_create_endpoint_ipv4(&src_ipv4
, &dst_ipv4
);
236 struct pcep_message
*path_request
=
237 pcep_msg_create_request(rp_object
, ep_object
, NULL
);
238 send_message(session
, path_request
, true);
241 void send_pce_report_message(pcep_session
*session
)
243 double_linked_list
*report_list
= dll_initialize();
245 /* SRP Path Setup Type TLV */
246 struct pcep_object_tlv_path_setup_type
*pst_tlv
=
247 pcep_tlv_create_path_setup_type(SR_TE_PST
);
248 double_linked_list
*srp_tlv_list
= dll_initialize();
249 dll_append(srp_tlv_list
, pst_tlv
);
252 * Create the SRP object
254 uint32_t srp_id_number
= 0x10203040;
255 struct pcep_object_header
*obj
=
256 (struct pcep_object_header
*)pcep_obj_create_srp(
257 false, srp_id_number
, srp_tlv_list
);
259 pcep_log(LOG_WARNING
,
260 "%s: send_pce_report_message SRP object was NULL",
262 dll_destroy_with_data(report_list
);
265 dll_append(report_list
, obj
);
267 /* LSP Symbolic path name TLV */
268 char symbolic_path_name
[] = "second-default";
269 struct pcep_object_tlv_symbolic_path_name
*spn_tlv
=
270 pcep_tlv_create_symbolic_path_name(symbolic_path_name
, 14);
271 double_linked_list
*lsp_tlv_list
= dll_initialize();
272 dll_append(lsp_tlv_list
, spn_tlv
);
274 /* LSP IPv4 LSP ID TLV */
275 struct in_addr ipv4_tunnel_sender
;
276 struct in_addr ipv4_tunnel_endpoint
;
277 inet_pton(AF_INET
, "9.9.1.1", &ipv4_tunnel_sender
);
278 inet_pton(AF_INET
, "9.9.2.1", &ipv4_tunnel_endpoint
);
279 struct pcep_object_tlv_ipv4_lsp_identifier
*ipv4_lsp_id_tlv
=
280 pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender
,
281 &ipv4_tunnel_endpoint
, 42,
283 dll_append(lsp_tlv_list
, ipv4_lsp_id_tlv
);
286 * Create the LSP object
288 uint32_t plsp_id
= 42;
289 enum pcep_lsp_operational_status lsp_status
=
290 PCEP_LSP_OPERATIONAL_ACTIVE
;
291 bool c_flag
= false; /* Lsp was created by PcInitiate msg */
292 bool a_flag
= false; /* Admin state, active / inactive */
293 bool r_flag
= false; /* true if LSP has been removed */
294 bool s_flag
= true; /* Synchronization */
295 bool d_flag
= false; /* Delegate LSP to PCE */
296 obj
= (struct pcep_object_header
*)pcep_obj_create_lsp(
297 plsp_id
, lsp_status
, c_flag
, a_flag
, r_flag
, s_flag
, d_flag
,
300 pcep_log(LOG_WARNING
,
301 "%s: send_pce_report_message LSP object was NULL",
303 dll_destroy_with_data(report_list
);
306 dll_append(report_list
, obj
);
308 /* Create 2 ERO NONAI sub-objects */
309 double_linked_list
*ero_subobj_list
= dll_initialize();
310 struct pcep_ro_subobj_sr
*sr_subobj_nonai1
=
311 pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
312 dll_append(ero_subobj_list
, sr_subobj_nonai1
);
314 struct pcep_ro_subobj_sr
*sr_subobj_nonai2
=
315 pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
316 dll_append(ero_subobj_list
, sr_subobj_nonai2
);
318 /* Create ERO IPv4 node sub-object */
319 struct in_addr sr_subobj_ipv4
;
320 inet_pton(AF_INET
, "9.9.9.1", &sr_subobj_ipv4
);
321 struct pcep_ro_subobj_sr
*sr_subobj_ipv4node
=
322 pcep_obj_create_ro_subobj_sr_ipv4_node(
323 false, false, false, true, 16060, &sr_subobj_ipv4
);
324 if (sr_subobj_ipv4node
== NULL
) {
325 pcep_log(LOG_WARNING
,
326 "%s: send_pce_report_message ERO sub-object was NULL",
330 dll_append(ero_subobj_list
, sr_subobj_ipv4node
);
333 * Create the ERO object
335 obj
= (struct pcep_object_header
*)pcep_obj_create_ero(ero_subobj_list
);
337 pcep_log(LOG_WARNING
,
338 "%s: send_pce_report_message ERO object was NULL",
340 dll_destroy_with_data(report_list
);
343 dll_append(report_list
, obj
);
346 * Create the Metric object
348 obj
= (struct pcep_object_header
*)pcep_obj_create_metric(
349 PCEP_METRIC_TE
, false, true, 16.0);
350 dll_append(report_list
, obj
);
352 /* Create and send the report message */
353 struct pcep_message
*report_msg
= pcep_msg_create_report(report_list
);
354 send_message(session
, report_msg
, true);
357 void print_queue_event(struct pcep_event
*event
)
361 "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
362 __func__
, time(NULL
), pthread_self(),
363 get_event_type_str(event
->event_type
),
364 event
->session
->session_id
, event
->event_time
);
366 if (event
->event_type
== MESSAGE_RECEIVED
) {
368 LOG_INFO
, "%s: \t Event message type [%s]", __func__
,
369 get_message_type_str(event
->message
->msg_header
->type
));
373 /* Called by pcep_session_logic when pcep_event's are ready */
374 void pcep_event_callback(void *cb_data
, pcep_event
*e
)
377 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] pcep_event_callback", __func__
,
378 time(NULL
), pthread_self());
379 pthread_mutex_lock(&pcep_event_mutex
);
381 pcep_event_condition
= true;
382 pthread_cond_signal(&pcep_event_cond_var
);
383 pthread_mutex_unlock(&pcep_event_mutex
);
386 int main(int argc
, char **argv
)
388 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] starting pcc_pcep example client",
389 __func__
, time(NULL
), pthread_self());
391 cmd_line_args
= get_cmdline_args(argc
, argv
);
392 if (cmd_line_args
== NULL
) {
398 if (cmd_line_args
->eventpoll
== false) {
399 struct pceplib_infra_config infra_config
;
400 memset(&infra_config
, 0, sizeof(infra_config
));
401 infra_config
.pcep_event_func
= pcep_event_callback
;
402 if (!initialize_pcc_infra(&infra_config
)) {
404 "%s: Error initializing PCC with infra.",
409 if (!initialize_pcc()) {
410 pcep_log(LOG_ERR
, "%s: Error initializing PCC.",
416 pcep_configuration
*config
= create_default_pcep_configuration();
417 config
->pcep_msg_versioning
->draft_ietf_pce_segment_routing_07
= true;
418 config
->src_pcep_port
= cmd_line_args
->src_tcp_port
;
419 config
->is_tcp_auth_md5
= true;
421 strlcpy(config
->tcp_authentication_str
, cmd_line_args
->tcp_md5_str
,
422 sizeof(config
->tcp_authentication_str
));
424 int af
= (cmd_line_args
->is_ipv6
? AF_INET6
: AF_INET
);
425 struct hostent
*host_info
=
426 gethostbyname2(cmd_line_args
->dest_ip_str
, af
);
427 if (host_info
== NULL
) {
428 pcep_log(LOG_ERR
, "%s: Error getting IP address.", __func__
);
432 if (cmd_line_args
->is_ipv6
) {
433 struct in6_addr host_address
;
434 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
435 session
= connect_pce_ipv6(config
, &host_address
);
437 struct in_addr host_address
;
438 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
439 session
= connect_pce(config
, &host_address
);
442 if (session
== NULL
) {
443 pcep_log(LOG_WARNING
, "%s: Error in connect_pce.", __func__
);
444 destroy_pcep_configuration(config
);
450 send_pce_report_message(session
);
451 /*send_pce_path_request_message(session);*/
453 /* Wait for pcep_event's either by polling the event queue or by
455 if (cmd_line_args
->eventpoll
== true) {
456 /* Poll the pcep_event queue*/
457 while (pcc_active_
) {
458 if (event_queue_is_empty() == false) {
459 struct pcep_event
*event
=
460 event_queue_get_event();
461 print_queue_event(event
);
462 destroy_pcep_event(event
);
468 /* Get events via callback and conditional variable */
469 pthread_mutex_init(&pcep_event_mutex
, NULL
);
470 pthread_cond_init(&pcep_event_cond_var
, NULL
);
471 while (pcc_active_
) {
472 pthread_mutex_lock(&pcep_event_mutex
);
474 /* this internal loop helps avoid spurious interrupts */
475 while (!pcep_event_condition
) {
476 pthread_cond_wait(&pcep_event_cond_var
,
480 /* Check if we have been interrupted by SIGINT */
482 print_queue_event(event
);
483 destroy_pcep_event(event
);
486 pcep_event_condition
= false;
487 pthread_mutex_unlock(&pcep_event_mutex
);
490 pthread_mutex_destroy(&pcep_event_mutex
);
491 pthread_cond_destroy(&pcep_event_cond_var
);
494 pcep_log(LOG_NOTICE
, "%s: Disconnecting from PCE", __func__
);
495 disconnect_pce(session
);
496 destroy_pcep_configuration(config
);
499 if (!destroy_pcc()) {
500 pcep_log(LOG_NOTICE
, "%s: Error stopping PCC.", __func__
);
503 pceplib_memory_dump();