2 * This file is part of the PCEPlib, a PCEP protocol library.
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * Author : Brady Johnson <brady@voltanet.io>
25 * Sample PCC implementation
30 #include <netdb.h> // gethostbyname
31 #include <netinet/tcp.h>
40 #include "pcep_pcc_api.h"
41 #include "pcep_utils_double_linked_list.h"
42 #include "pcep_utils_logging.h"
43 #include "pcep_utils_memory.h"
46 * PCEP PCC design spec:
47 * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
49 #define MAX_SRC_IP_STR 40
50 #define MAX_DST_IP_STR 40
51 struct cmd_line_args
{
52 char src_ip_str
[MAX_SRC_IP_STR
];
53 char dest_ip_str
[MAX_DST_IP_STR
];
56 char tcp_md5_str
[PCEP_MD5SIG_MAXKEYLEN
]; /* RFC 2385 */
58 bool eventpoll
; /* poll for pcep_event's, or use callback (default) */
61 bool pcc_active_
= true;
62 pcep_session
*session
= NULL
;
63 struct cmd_line_args
*cmd_line_args
= NULL
;
64 /* pcep_event callback variables */
65 bool pcep_event_condition
= false;
66 struct pcep_event
*event
= NULL
;
67 pthread_mutex_t pcep_event_mutex
;
68 pthread_cond_t pcep_event_cond_var
;
70 static const char DEFAULT_DEST_HOSTNAME
[] = "localhost";
71 static const char DEFAULT_DEST_HOSTNAME_IPV6
[] = "ip6-localhost";
72 static const short DEFAULT_SRC_TCP_PORT
= 4999;
75 struct cmd_line_args
*get_cmdline_args(int argc
, char *argv
[]);
76 void handle_signal_action(int sig_number
);
77 int setup_signals(void);
78 void send_pce_path_request_message(pcep_session
*session
);
79 void send_pce_report_message(pcep_session
*session
);
80 void print_queue_event(struct pcep_event
*event
);
81 void pcep_event_callback(void *cb_data
, pcep_event
*e
);
83 struct cmd_line_args
*get_cmdline_args(int argc
, char *argv
[])
85 /* Allocate and set default values */
86 struct cmd_line_args
*cmd_line_args
=
87 malloc(sizeof(struct cmd_line_args
));
88 memset(cmd_line_args
, 0, sizeof(struct cmd_line_args
));
89 strlcpy(cmd_line_args
->dest_ip_str
, DEFAULT_DEST_HOSTNAME
,
91 cmd_line_args
->src_tcp_port
= DEFAULT_SRC_TCP_PORT
;
92 cmd_line_args
->is_ipv6
= false;
94 /* Parse the cmd_line args:
103 for (; i
< argc
; ++i
) {
104 if (strcmp(argv
[i
], "-help") == 0
105 || strcmp(argv
[i
], "--help") == 0
106 || strcmp(argv
[i
], "-h") == 0) {
109 "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
113 } else if (strcmp(argv
[i
], "-ipv6") == 0) {
114 cmd_line_args
->is_ipv6
= true;
116 strlcpy(cmd_line_args
->dest_ip_str
,
117 DEFAULT_DEST_HOSTNAME_IPV6
,
120 } else if (strcmp(argv
[i
], "-eventpoll") == 0) {
121 cmd_line_args
->eventpoll
= true;
122 } else if (strcmp(argv
[i
], "-srcip") == 0) {
124 strlcpy(cmd_line_args
->src_ip_str
, argv
[++i
],
129 "%s: Invalid number of cmd_line_args for \"-srcip\"",
134 } else if (strcmp(argv
[i
], "-destip") == 0) {
136 strlcpy(cmd_line_args
->dest_ip_str
, argv
[++i
],
141 "%s: Invalid number of cmd_line_args for \"-destip\"",
146 } else if (strcmp(argv
[i
], "-srcport") == 0) {
148 cmd_line_args
->src_tcp_port
= atoi(argv
[++i
]);
152 "%s: Invalid number of cmd_line_args for \"-srcport\"",
157 } else if (strcmp(argv
[i
], "-destport") == 0) {
159 cmd_line_args
->dest_tcp_port
= atoi(argv
[++i
]);
163 "%s: Invalid number of cmd_line_args for \"-destport\"",
168 } else if (strcmp(argv
[i
], "-tcpmd5") == 0) {
170 strlcpy(cmd_line_args
->tcp_md5_str
, argv
[++i
],
171 sizeof(cmd_line_args
->tcp_md5_str
));
175 "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
181 pcep_log(LOG_ERR
, "%s: Invalid cmd_line_arg[%d] = %s",
182 __func__
, i
, argv
[i
]);
188 return cmd_line_args
;
191 void handle_signal_action(int sig_number
)
193 if (sig_number
== SIGINT
) {
194 pcep_log(LOG_INFO
, "%s: SIGINT was caught!", __func__
);
196 if (cmd_line_args
->eventpoll
== false) {
197 pthread_mutex_lock(&pcep_event_mutex
);
198 pcep_event_condition
= true;
199 pthread_cond_signal(&pcep_event_cond_var
);
200 pthread_mutex_unlock(&pcep_event_mutex
);
202 } else if (sig_number
== SIGUSR1
) {
203 pcep_log(LOG_INFO
, "%s: SIGUSR1 was caught, dumping counters",
205 dump_pcep_session_counters(session
);
206 pceplib_memory_dump();
207 } else if (sig_number
== SIGUSR2
) {
208 pcep_log(LOG_INFO
, "%s: SIGUSR2 was caught, reseting counters",
210 reset_pcep_session_counters(session
);
218 memset(&sa
, 0, sizeof(struct sigaction
));
219 sa
.sa_handler
= handle_signal_action
;
220 if (sigaction(SIGINT
, &sa
, 0) != 0) {
221 perror("sigaction()");
225 if (sigaction(SIGUSR1
, &sa
, 0) != 0) {
226 perror("sigaction()");
230 if (sigaction(SIGUSR2
, &sa
, 0) != 0) {
231 perror("sigaction()");
238 void send_pce_path_request_message(pcep_session
*session
)
240 struct in_addr src_ipv4
;
241 struct in_addr dst_ipv4
;
242 inet_pton(AF_INET
, "1.2.3.4", &src_ipv4
);
243 inet_pton(AF_INET
, "10.20.30.40", &dst_ipv4
);
245 struct pcep_object_rp
*rp_object
=
246 pcep_obj_create_rp(1, false, false, false, false, 42, NULL
);
247 struct pcep_object_endpoints_ipv4
*ep_object
=
248 pcep_obj_create_endpoint_ipv4(&src_ipv4
, &dst_ipv4
);
250 struct pcep_message
*path_request
=
251 pcep_msg_create_request(rp_object
, ep_object
, NULL
);
252 send_message(session
, path_request
, true);
255 void send_pce_report_message(pcep_session
*session
)
257 double_linked_list
*report_list
= dll_initialize();
259 /* SRP Path Setup Type TLV */
260 struct pcep_object_tlv_path_setup_type
*pst_tlv
=
261 pcep_tlv_create_path_setup_type(SR_TE_PST
);
262 double_linked_list
*srp_tlv_list
= dll_initialize();
263 dll_append(srp_tlv_list
, pst_tlv
);
266 * Create the SRP object
268 uint32_t srp_id_number
= 0x10203040;
269 struct pcep_object_header
*obj
=
270 (struct pcep_object_header
*)pcep_obj_create_srp(
271 false, srp_id_number
, srp_tlv_list
);
273 pcep_log(LOG_WARNING
,
274 "%s: send_pce_report_message SRP object was NULL",
276 dll_destroy_with_data(report_list
);
279 dll_append(report_list
, obj
);
281 /* LSP Symbolic path name TLV */
282 char symbolic_path_name
[] = "second-default";
283 struct pcep_object_tlv_symbolic_path_name
*spn_tlv
=
284 pcep_tlv_create_symbolic_path_name(symbolic_path_name
, 14);
285 double_linked_list
*lsp_tlv_list
= dll_initialize();
286 dll_append(lsp_tlv_list
, spn_tlv
);
288 /* LSP IPv4 LSP ID TLV */
289 struct in_addr ipv4_tunnel_sender
;
290 struct in_addr ipv4_tunnel_endpoint
;
291 inet_pton(AF_INET
, "9.9.1.1", &ipv4_tunnel_sender
);
292 inet_pton(AF_INET
, "9.9.2.1", &ipv4_tunnel_endpoint
);
293 struct pcep_object_tlv_ipv4_lsp_identifier
*ipv4_lsp_id_tlv
=
294 pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender
,
295 &ipv4_tunnel_endpoint
, 42,
297 dll_append(lsp_tlv_list
, ipv4_lsp_id_tlv
);
300 * Create the LSP object
302 uint32_t plsp_id
= 42;
303 enum pcep_lsp_operational_status lsp_status
=
304 PCEP_LSP_OPERATIONAL_ACTIVE
;
305 bool c_flag
= false; /* Lsp was created by PcInitiate msg */
306 bool a_flag
= false; /* Admin state, active / inactive */
307 bool r_flag
= false; /* true if LSP has been removed */
308 bool s_flag
= true; /* Synchronization */
309 bool d_flag
= false; /* Delegate LSP to PCE */
310 obj
= (struct pcep_object_header
*)pcep_obj_create_lsp(
311 plsp_id
, lsp_status
, c_flag
, a_flag
, r_flag
, s_flag
, d_flag
,
314 pcep_log(LOG_WARNING
,
315 "%s: send_pce_report_message LSP object was NULL",
317 dll_destroy_with_data(report_list
);
320 dll_append(report_list
, obj
);
322 /* Create 2 ERO NONAI sub-objects */
323 double_linked_list
*ero_subobj_list
= dll_initialize();
324 struct pcep_ro_subobj_sr
*sr_subobj_nonai1
=
325 pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
326 dll_append(ero_subobj_list
, sr_subobj_nonai1
);
328 struct pcep_ro_subobj_sr
*sr_subobj_nonai2
=
329 pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
330 dll_append(ero_subobj_list
, sr_subobj_nonai2
);
332 /* Create ERO IPv4 node sub-object */
333 struct in_addr sr_subobj_ipv4
;
334 inet_pton(AF_INET
, "9.9.9.1", &sr_subobj_ipv4
);
335 struct pcep_ro_subobj_sr
*sr_subobj_ipv4node
=
336 pcep_obj_create_ro_subobj_sr_ipv4_node(
337 false, false, false, true, 16060, &sr_subobj_ipv4
);
338 if (sr_subobj_ipv4node
== NULL
) {
339 pcep_log(LOG_WARNING
,
340 "%s: send_pce_report_message ERO sub-object was NULL",
344 dll_append(ero_subobj_list
, sr_subobj_ipv4node
);
347 * Create the ERO object
349 obj
= (struct pcep_object_header
*)pcep_obj_create_ero(ero_subobj_list
);
351 pcep_log(LOG_WARNING
,
352 "%s: send_pce_report_message ERO object was NULL",
354 dll_destroy_with_data(report_list
);
357 dll_append(report_list
, obj
);
360 * Create the Metric object
362 obj
= (struct pcep_object_header
*)pcep_obj_create_metric(
363 PCEP_METRIC_TE
, false, true, 16.0);
364 dll_append(report_list
, obj
);
366 /* Create and send the report message */
367 struct pcep_message
*report_msg
= pcep_msg_create_report(report_list
);
368 send_message(session
, report_msg
, true);
371 void print_queue_event(struct pcep_event
*event
)
375 "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
376 __func__
, time(NULL
), pthread_self(),
377 get_event_type_str(event
->event_type
),
378 event
->session
->session_id
, event
->event_time
);
380 if (event
->event_type
== MESSAGE_RECEIVED
) {
382 LOG_INFO
, "%s: \t Event message type [%s]", __func__
,
383 get_message_type_str(event
->message
->msg_header
->type
));
387 /* Called by pcep_session_logic when pcep_event's are ready */
388 void pcep_event_callback(void *cb_data
, pcep_event
*e
)
391 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] pcep_event_callback", __func__
,
392 time(NULL
), pthread_self());
393 pthread_mutex_lock(&pcep_event_mutex
);
395 pcep_event_condition
= true;
396 pthread_cond_signal(&pcep_event_cond_var
);
397 pthread_mutex_unlock(&pcep_event_mutex
);
400 int main(int argc
, char **argv
)
402 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] starting pcc_pcep example client",
403 __func__
, time(NULL
), pthread_self());
405 cmd_line_args
= get_cmdline_args(argc
, argv
);
406 if (cmd_line_args
== NULL
) {
412 if (cmd_line_args
->eventpoll
== false) {
413 struct pceplib_infra_config infra_config
;
414 memset(&infra_config
, 0, sizeof(infra_config
));
415 infra_config
.pcep_event_func
= pcep_event_callback
;
416 if (!initialize_pcc_infra(&infra_config
)) {
418 "%s: Error initializing PCC with infra.",
423 if (!initialize_pcc()) {
424 pcep_log(LOG_ERR
, "%s: Error initializing PCC.",
430 pcep_configuration
*config
= create_default_pcep_configuration();
431 config
->pcep_msg_versioning
->draft_ietf_pce_segment_routing_07
= true;
432 config
->src_pcep_port
= cmd_line_args
->src_tcp_port
;
433 config
->is_tcp_auth_md5
= true;
435 strlcpy(config
->tcp_authentication_str
, cmd_line_args
->tcp_md5_str
,
436 sizeof(config
->tcp_authentication_str
));
438 int af
= (cmd_line_args
->is_ipv6
? AF_INET6
: AF_INET
);
439 struct hostent
*host_info
=
440 gethostbyname2(cmd_line_args
->dest_ip_str
, af
);
441 if (host_info
== NULL
) {
442 pcep_log(LOG_ERR
, "%s: Error getting IP address.", __func__
);
446 if (cmd_line_args
->is_ipv6
) {
447 struct in6_addr host_address
;
448 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
449 session
= connect_pce_ipv6(config
, &host_address
);
451 struct in_addr host_address
;
452 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
453 session
= connect_pce(config
, &host_address
);
456 if (session
== NULL
) {
457 pcep_log(LOG_WARNING
, "%s: Error in connect_pce.", __func__
);
458 destroy_pcep_configuration(config
);
464 send_pce_report_message(session
);
465 /*send_pce_path_request_message(session);*/
467 /* Wait for pcep_event's either by polling the event queue or by
469 if (cmd_line_args
->eventpoll
== true) {
470 /* Poll the pcep_event queue*/
471 while (pcc_active_
) {
472 if (event_queue_is_empty() == false) {
473 struct pcep_event
*event
=
474 event_queue_get_event();
475 print_queue_event(event
);
476 destroy_pcep_event(event
);
482 /* Get events via callback and conditional variable */
483 pthread_mutex_init(&pcep_event_mutex
, NULL
);
484 pthread_cond_init(&pcep_event_cond_var
, NULL
);
485 while (pcc_active_
) {
486 pthread_mutex_lock(&pcep_event_mutex
);
488 /* this internal loop helps avoid spurious interrupts */
489 while (!pcep_event_condition
) {
490 pthread_cond_wait(&pcep_event_cond_var
,
494 /* Check if we have been interrupted by SIGINT */
496 print_queue_event(event
);
497 destroy_pcep_event(event
);
500 pcep_event_condition
= false;
501 pthread_mutex_unlock(&pcep_event_mutex
);
504 pthread_mutex_destroy(&pcep_event_mutex
);
505 pthread_cond_destroy(&pcep_event_cond_var
);
508 pcep_log(LOG_NOTICE
, "%s: Disconnecting from PCE", __func__
);
509 disconnect_pce(session
);
510 destroy_pcep_configuration(config
);
513 if (!destroy_pcc()) {
514 pcep_log(LOG_NOTICE
, "%s: Error stopping PCC.", __func__
);
517 pceplib_memory_dump();