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
[TCP_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",
278 dll_append(report_list
, obj
);
280 /* LSP Symbolic path name TLV */
281 char symbolic_path_name
[] = "second-default";
282 struct pcep_object_tlv_symbolic_path_name
*spn_tlv
=
283 pcep_tlv_create_symbolic_path_name(symbolic_path_name
, 14);
284 double_linked_list
*lsp_tlv_list
= dll_initialize();
285 dll_append(lsp_tlv_list
, spn_tlv
);
287 /* LSP IPv4 LSP ID TLV */
288 struct in_addr ipv4_tunnel_sender
;
289 struct in_addr ipv4_tunnel_endpoint
;
290 inet_pton(AF_INET
, "9.9.1.1", &ipv4_tunnel_sender
);
291 inet_pton(AF_INET
, "9.9.2.1", &ipv4_tunnel_endpoint
);
292 struct pcep_object_tlv_ipv4_lsp_identifier
*ipv4_lsp_id_tlv
=
293 pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender
,
294 &ipv4_tunnel_endpoint
, 42,
296 dll_append(lsp_tlv_list
, ipv4_lsp_id_tlv
);
299 * Create the LSP object
301 uint32_t plsp_id
= 42;
302 enum pcep_lsp_operational_status lsp_status
=
303 PCEP_LSP_OPERATIONAL_ACTIVE
;
304 bool c_flag
= false; /* Lsp was created by PcInitiate msg */
305 bool a_flag
= false; /* Admin state, active / inactive */
306 bool r_flag
= false; /* true if LSP has been removed */
307 bool s_flag
= true; /* Synchronization */
308 bool d_flag
= false; /* Delegate LSP to PCE */
309 obj
= (struct pcep_object_header
*)pcep_obj_create_lsp(
310 plsp_id
, lsp_status
, c_flag
, a_flag
, r_flag
, s_flag
, d_flag
,
313 pcep_log(LOG_WARNING
,
314 "%s: send_pce_report_message LSP object was NULL",
318 dll_append(report_list
, obj
);
320 /* Create 2 ERO NONAI sub-objects */
321 double_linked_list
*ero_subobj_list
= dll_initialize();
322 struct pcep_ro_subobj_sr
*sr_subobj_nonai1
=
323 pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
324 dll_append(ero_subobj_list
, sr_subobj_nonai1
);
326 struct pcep_ro_subobj_sr
*sr_subobj_nonai2
=
327 pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
328 dll_append(ero_subobj_list
, sr_subobj_nonai2
);
330 /* Create ERO IPv4 node sub-object */
331 struct in_addr sr_subobj_ipv4
;
332 inet_pton(AF_INET
, "9.9.9.1", &sr_subobj_ipv4
);
333 struct pcep_ro_subobj_sr
*sr_subobj_ipv4node
=
334 pcep_obj_create_ro_subobj_sr_ipv4_node(
335 false, false, false, true, 16060, &sr_subobj_ipv4
);
336 if (sr_subobj_ipv4node
== NULL
) {
337 pcep_log(LOG_WARNING
,
338 "%s: send_pce_report_message ERO sub-object was NULL",
342 dll_append(ero_subobj_list
, sr_subobj_ipv4node
);
345 * Create the ERO object
347 obj
= (struct pcep_object_header
*)pcep_obj_create_ero(ero_subobj_list
);
349 pcep_log(LOG_WARNING
,
350 "%s: send_pce_report_message ERO object was NULL",
354 dll_append(report_list
, obj
);
357 * Create the Metric object
359 obj
= (struct pcep_object_header
*)pcep_obj_create_metric(
360 PCEP_METRIC_TE
, false, true, 16.0);
361 dll_append(report_list
, obj
);
363 /* Create and send the report message */
364 struct pcep_message
*report_msg
= pcep_msg_create_report(report_list
);
365 send_message(session
, report_msg
, true);
368 void print_queue_event(struct pcep_event
*event
)
372 "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
373 __func__
, time(NULL
), pthread_self(),
374 get_event_type_str(event
->event_type
),
375 event
->session
->session_id
, event
->event_time
);
377 if (event
->event_type
== MESSAGE_RECEIVED
) {
379 LOG_INFO
, "%s: \t Event message type [%s]", __func__
,
380 get_message_type_str(event
->message
->msg_header
->type
));
384 /* Called by pcep_session_logic when pcep_event's are ready */
385 void pcep_event_callback(void *cb_data
, pcep_event
*e
)
388 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] pcep_event_callback", __func__
,
389 time(NULL
), pthread_self());
390 pthread_mutex_lock(&pcep_event_mutex
);
392 pcep_event_condition
= true;
393 pthread_cond_signal(&pcep_event_cond_var
);
394 pthread_mutex_unlock(&pcep_event_mutex
);
397 int main(int argc
, char **argv
)
399 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] starting pcc_pcep example client",
400 __func__
, time(NULL
), pthread_self());
402 cmd_line_args
= get_cmdline_args(argc
, argv
);
403 if (cmd_line_args
== NULL
) {
409 if (cmd_line_args
->eventpoll
== false) {
410 struct pceplib_infra_config infra_config
;
411 memset(&infra_config
, 0, sizeof(infra_config
));
412 infra_config
.pcep_event_func
= pcep_event_callback
;
413 if (!initialize_pcc_infra(&infra_config
)) {
415 "%s: Error initializing PCC with infra.",
420 if (!initialize_pcc()) {
421 pcep_log(LOG_ERR
, "%s: Error initializing PCC.",
427 pcep_configuration
*config
= create_default_pcep_configuration();
428 config
->pcep_msg_versioning
->draft_ietf_pce_segment_routing_07
= true;
429 config
->src_pcep_port
= cmd_line_args
->src_tcp_port
;
430 config
->is_tcp_auth_md5
= true;
432 strlcpy(config
->tcp_authentication_str
, cmd_line_args
->tcp_md5_str
,
433 sizeof(config
->tcp_authentication_str
));
435 int af
= (cmd_line_args
->is_ipv6
? AF_INET6
: AF_INET
);
436 struct hostent
*host_info
=
437 gethostbyname2(cmd_line_args
->dest_ip_str
, af
);
438 if (host_info
== NULL
) {
439 pcep_log(LOG_ERR
, "%s: Error getting IP address.", __func__
);
443 if (cmd_line_args
->is_ipv6
) {
444 struct in6_addr host_address
;
445 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
446 session
= connect_pce_ipv6(config
, &host_address
);
448 struct in_addr host_address
;
449 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
450 session
= connect_pce(config
, &host_address
);
453 if (session
== NULL
) {
454 pcep_log(LOG_WARNING
, "%s: Error in connect_pce.", __func__
);
455 destroy_pcep_configuration(config
);
461 send_pce_report_message(session
);
462 /*send_pce_path_request_message(session);*/
464 /* Wait for pcep_event's either by polling the event queue or by
466 if (cmd_line_args
->eventpoll
== true) {
467 /* Poll the pcep_event queue*/
468 while (pcc_active_
) {
469 if (event_queue_is_empty() == false) {
470 struct pcep_event
*event
=
471 event_queue_get_event();
472 print_queue_event(event
);
473 destroy_pcep_event(event
);
479 /* Get events via callback and conditional variable */
480 pthread_mutex_init(&pcep_event_mutex
, NULL
);
481 pthread_cond_init(&pcep_event_cond_var
, NULL
);
482 while (pcc_active_
) {
483 pthread_mutex_lock(&pcep_event_mutex
);
485 /* this internal loop helps avoid spurious interrupts */
486 while (!pcep_event_condition
) {
487 pthread_cond_wait(&pcep_event_cond_var
,
491 /* Check if we have been interrupted by SIGINT */
493 print_queue_event(event
);
494 destroy_pcep_event(event
);
497 pcep_event_condition
= false;
498 pthread_mutex_unlock(&pcep_event_mutex
);
501 pthread_mutex_destroy(&pcep_event_mutex
);
502 pthread_cond_destroy(&pcep_event_cond_var
);
505 pcep_log(LOG_NOTICE
, "%s: Disconnecting from PCE", __func__
);
506 disconnect_pce(session
);
507 destroy_pcep_configuration(config
);
510 if (!destroy_pcc()) {
511 pcep_log(LOG_NOTICE
, "%s: Error stopping PCC.", __func__
);
514 pceplib_memory_dump();