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 void send_pce_path_request_message(pcep_session
*session
);
78 void send_pce_report_message(pcep_session
*session
);
79 void print_queue_event(struct pcep_event
*event
);
80 void pcep_event_callback(void *cb_data
, pcep_event
*e
);
82 struct cmd_line_args
*get_cmdline_args(int argc
, char *argv
[])
84 /* Allocate and set default values */
85 struct cmd_line_args
*cmd_line_args
=
86 malloc(sizeof(struct cmd_line_args
));
87 memset(cmd_line_args
, 0, sizeof(struct cmd_line_args
));
88 strlcpy(cmd_line_args
->dest_ip_str
, DEFAULT_DEST_HOSTNAME
,
90 cmd_line_args
->src_tcp_port
= DEFAULT_SRC_TCP_PORT
;
91 cmd_line_args
->is_ipv6
= false;
93 /* Parse the cmd_line args:
102 for (; i
< argc
; ++i
) {
103 if (strcmp(argv
[i
], "-help") == 0
104 || strcmp(argv
[i
], "--help") == 0
105 || strcmp(argv
[i
], "-h") == 0) {
108 "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
112 } else if (strcmp(argv
[i
], "-ipv6") == 0) {
113 cmd_line_args
->is_ipv6
= true;
115 strlcpy(cmd_line_args
->dest_ip_str
,
116 DEFAULT_DEST_HOSTNAME_IPV6
,
119 } else if (strcmp(argv
[i
], "-eventpoll") == 0) {
120 cmd_line_args
->eventpoll
= true;
121 } else if (strcmp(argv
[i
], "-srcip") == 0) {
123 strlcpy(cmd_line_args
->src_ip_str
, argv
[++i
],
128 "%s: Invalid number of cmd_line_args for \"-srcip\"",
133 } else if (strcmp(argv
[i
], "-destip") == 0) {
135 strlcpy(cmd_line_args
->dest_ip_str
, argv
[++i
],
140 "%s: Invalid number of cmd_line_args for \"-destip\"",
145 } else if (strcmp(argv
[i
], "-srcport") == 0) {
147 cmd_line_args
->src_tcp_port
= atoi(argv
[++i
]);
151 "%s: Invalid number of cmd_line_args for \"-srcport\"",
156 } else if (strcmp(argv
[i
], "-destport") == 0) {
158 cmd_line_args
->dest_tcp_port
= atoi(argv
[++i
]);
162 "%s: Invalid number of cmd_line_args for \"-destport\"",
167 } else if (strcmp(argv
[i
], "-tcpmd5") == 0) {
169 strlcpy(cmd_line_args
->tcp_md5_str
, argv
[++i
],
170 sizeof(cmd_line_args
->tcp_md5_str
));
174 "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
180 pcep_log(LOG_ERR
, "%s: Invalid cmd_line_arg[%d] = %s",
181 __func__
, i
, argv
[i
]);
187 return cmd_line_args
;
190 void handle_signal_action(int sig_number
)
192 if (sig_number
== SIGINT
) {
193 pcep_log(LOG_INFO
, "%s: SIGINT was caught!", __func__
);
195 if (cmd_line_args
->eventpoll
== false) {
196 pthread_mutex_lock(&pcep_event_mutex
);
197 pcep_event_condition
= true;
198 pthread_cond_signal(&pcep_event_cond_var
);
199 pthread_mutex_unlock(&pcep_event_mutex
);
201 } else if (sig_number
== SIGUSR1
) {
202 pcep_log(LOG_INFO
, "%s: SIGUSR1 was caught, dumping counters",
204 dump_pcep_session_counters(session
);
205 pceplib_memory_dump();
206 } else if (sig_number
== SIGUSR2
) {
207 pcep_log(LOG_INFO
, "%s: SIGUSR2 was caught, reseting counters",
209 reset_pcep_session_counters(session
);
213 static int setup_signals(void)
216 memset(&sa
, 0, sizeof(sa
));
217 sa
.sa_handler
= handle_signal_action
;
218 if (sigaction(SIGINT
, &sa
, 0) != 0) {
219 perror("sigaction()");
223 if (sigaction(SIGUSR1
, &sa
, 0) != 0) {
224 perror("sigaction()");
228 if (sigaction(SIGUSR2
, &sa
, 0) != 0) {
229 perror("sigaction()");
236 void send_pce_path_request_message(pcep_session
*session
)
238 struct in_addr src_ipv4
;
239 struct in_addr dst_ipv4
;
240 inet_pton(AF_INET
, "1.2.3.4", &src_ipv4
);
241 inet_pton(AF_INET
, "10.20.30.40", &dst_ipv4
);
243 struct pcep_object_rp
*rp_object
=
244 pcep_obj_create_rp(1, false, false, false, false, 42, NULL
);
245 struct pcep_object_endpoints_ipv4
*ep_object
=
246 pcep_obj_create_endpoint_ipv4(&src_ipv4
, &dst_ipv4
);
248 struct pcep_message
*path_request
=
249 pcep_msg_create_request(rp_object
, ep_object
, NULL
);
250 send_message(session
, path_request
, true);
253 void send_pce_report_message(pcep_session
*session
)
255 double_linked_list
*report_list
= dll_initialize();
257 /* SRP Path Setup Type TLV */
258 struct pcep_object_tlv_path_setup_type
*pst_tlv
=
259 pcep_tlv_create_path_setup_type(SR_TE_PST
);
260 double_linked_list
*srp_tlv_list
= dll_initialize();
261 dll_append(srp_tlv_list
, pst_tlv
);
264 * Create the SRP object
266 uint32_t srp_id_number
= 0x10203040;
267 struct pcep_object_header
*obj
=
268 (struct pcep_object_header
*)pcep_obj_create_srp(
269 false, srp_id_number
, srp_tlv_list
);
271 pcep_log(LOG_WARNING
,
272 "%s: send_pce_report_message SRP object was NULL",
274 dll_destroy_with_data(report_list
);
277 dll_append(report_list
, obj
);
279 /* LSP Symbolic path name TLV */
280 char symbolic_path_name
[] = "second-default";
281 struct pcep_object_tlv_symbolic_path_name
*spn_tlv
=
282 pcep_tlv_create_symbolic_path_name(symbolic_path_name
, 14);
283 double_linked_list
*lsp_tlv_list
= dll_initialize();
284 dll_append(lsp_tlv_list
, spn_tlv
);
286 /* LSP IPv4 LSP ID TLV */
287 struct in_addr ipv4_tunnel_sender
;
288 struct in_addr ipv4_tunnel_endpoint
;
289 inet_pton(AF_INET
, "9.9.1.1", &ipv4_tunnel_sender
);
290 inet_pton(AF_INET
, "9.9.2.1", &ipv4_tunnel_endpoint
);
291 struct pcep_object_tlv_ipv4_lsp_identifier
*ipv4_lsp_id_tlv
=
292 pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender
,
293 &ipv4_tunnel_endpoint
, 42,
295 dll_append(lsp_tlv_list
, ipv4_lsp_id_tlv
);
298 * Create the LSP object
300 uint32_t plsp_id
= 42;
301 enum pcep_lsp_operational_status lsp_status
=
302 PCEP_LSP_OPERATIONAL_ACTIVE
;
303 bool c_flag
= false; /* Lsp was created by PcInitiate msg */
304 bool a_flag
= false; /* Admin state, active / inactive */
305 bool r_flag
= false; /* true if LSP has been removed */
306 bool s_flag
= true; /* Synchronization */
307 bool d_flag
= false; /* Delegate LSP to PCE */
308 obj
= (struct pcep_object_header
*)pcep_obj_create_lsp(
309 plsp_id
, lsp_status
, c_flag
, a_flag
, r_flag
, s_flag
, d_flag
,
312 pcep_log(LOG_WARNING
,
313 "%s: send_pce_report_message LSP object was NULL",
315 dll_destroy_with_data(report_list
);
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",
352 dll_destroy_with_data(report_list
);
355 dll_append(report_list
, obj
);
358 * Create the Metric object
360 obj
= (struct pcep_object_header
*)pcep_obj_create_metric(
361 PCEP_METRIC_TE
, false, true, 16.0);
362 dll_append(report_list
, obj
);
364 /* Create and send the report message */
365 struct pcep_message
*report_msg
= pcep_msg_create_report(report_list
);
366 send_message(session
, report_msg
, true);
369 void print_queue_event(struct pcep_event
*event
)
373 "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
374 __func__
, time(NULL
), pthread_self(),
375 get_event_type_str(event
->event_type
),
376 event
->session
->session_id
, event
->event_time
);
378 if (event
->event_type
== MESSAGE_RECEIVED
) {
380 LOG_INFO
, "%s: \t Event message type [%s]", __func__
,
381 get_message_type_str(event
->message
->msg_header
->type
));
385 /* Called by pcep_session_logic when pcep_event's are ready */
386 void pcep_event_callback(void *cb_data
, pcep_event
*e
)
389 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] pcep_event_callback", __func__
,
390 time(NULL
), pthread_self());
391 pthread_mutex_lock(&pcep_event_mutex
);
393 pcep_event_condition
= true;
394 pthread_cond_signal(&pcep_event_cond_var
);
395 pthread_mutex_unlock(&pcep_event_mutex
);
398 int main(int argc
, char **argv
)
400 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] starting pcc_pcep example client",
401 __func__
, time(NULL
), pthread_self());
403 cmd_line_args
= get_cmdline_args(argc
, argv
);
404 if (cmd_line_args
== NULL
) {
410 if (cmd_line_args
->eventpoll
== false) {
411 struct pceplib_infra_config infra_config
;
412 memset(&infra_config
, 0, sizeof(infra_config
));
413 infra_config
.pcep_event_func
= pcep_event_callback
;
414 if (!initialize_pcc_infra(&infra_config
)) {
416 "%s: Error initializing PCC with infra.",
421 if (!initialize_pcc()) {
422 pcep_log(LOG_ERR
, "%s: Error initializing PCC.",
428 pcep_configuration
*config
= create_default_pcep_configuration();
429 config
->pcep_msg_versioning
->draft_ietf_pce_segment_routing_07
= true;
430 config
->src_pcep_port
= cmd_line_args
->src_tcp_port
;
431 config
->is_tcp_auth_md5
= true;
433 strlcpy(config
->tcp_authentication_str
, cmd_line_args
->tcp_md5_str
,
434 sizeof(config
->tcp_authentication_str
));
436 int af
= (cmd_line_args
->is_ipv6
? AF_INET6
: AF_INET
);
437 struct hostent
*host_info
=
438 gethostbyname2(cmd_line_args
->dest_ip_str
, af
);
439 if (host_info
== NULL
) {
440 pcep_log(LOG_ERR
, "%s: Error getting IP address.", __func__
);
444 if (cmd_line_args
->is_ipv6
) {
445 struct in6_addr host_address
;
446 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
447 session
= connect_pce_ipv6(config
, &host_address
);
449 struct in_addr host_address
;
450 memcpy(&host_address
, host_info
->h_addr
, host_info
->h_length
);
451 session
= connect_pce(config
, &host_address
);
454 if (session
== NULL
) {
455 pcep_log(LOG_WARNING
, "%s: Error in connect_pce.", __func__
);
456 destroy_pcep_configuration(config
);
462 send_pce_report_message(session
);
463 /*send_pce_path_request_message(session);*/
465 /* Wait for pcep_event's either by polling the event queue or by
467 if (cmd_line_args
->eventpoll
== true) {
468 /* Poll the pcep_event queue*/
469 while (pcc_active_
) {
470 if (event_queue_is_empty() == false) {
471 struct pcep_event
*event
=
472 event_queue_get_event();
473 print_queue_event(event
);
474 destroy_pcep_event(event
);
480 /* Get events via callback and conditional variable */
481 pthread_mutex_init(&pcep_event_mutex
, NULL
);
482 pthread_cond_init(&pcep_event_cond_var
, NULL
);
483 while (pcc_active_
) {
484 pthread_mutex_lock(&pcep_event_mutex
);
486 /* this internal loop helps avoid spurious interrupts */
487 while (!pcep_event_condition
) {
488 pthread_cond_wait(&pcep_event_cond_var
,
492 /* Check if we have been interrupted by SIGINT */
494 print_queue_event(event
);
495 destroy_pcep_event(event
);
498 pcep_event_condition
= false;
499 pthread_mutex_unlock(&pcep_event_mutex
);
502 pthread_mutex_destroy(&pcep_event_mutex
);
503 pthread_cond_destroy(&pcep_event_cond_var
);
506 pcep_log(LOG_NOTICE
, "%s: Disconnecting from PCE", __func__
);
507 disconnect_pce(session
);
508 destroy_pcep_configuration(config
);
511 if (!destroy_pcc()) {
512 pcep_log(LOG_NOTICE
, "%s: Error stopping PCC.", __func__
);
515 pceplib_memory_dump();