]> git.proxmox.com Git - mirror_frr.git/blame - pceplib/pcep_pcc.c
Merge pull request #11150 from opensourcerouting/fix/use_librtr_higher_0.8.0
[mirror_frr.git] / pceplib / pcep_pcc.c
CommitLineData
74971473
JG
1/*
2 * This file is part of the PCEPlib, a PCEP protocol library.
3 *
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author : Brady Johnson <brady@voltanet.io>
20 *
21 */
22
23
24/*
25 * Sample PCC implementation
26 */
27
28#include <zebra.h>
29
30#include <netdb.h> // gethostbyname
31#include <netinet/tcp.h>
32#include <pthread.h>
33#include <signal.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <time.h>
38#include <unistd.h>
39
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"
44
45/*
46 * PCEP PCC design spec:
47 * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
48 */
49#define MAX_SRC_IP_STR 40
50#define MAX_DST_IP_STR 40
51struct cmd_line_args {
52 char src_ip_str[MAX_SRC_IP_STR];
53 char dest_ip_str[MAX_DST_IP_STR];
54 short src_tcp_port;
55 short dest_tcp_port;
2a034138 56 char tcp_md5_str[PCEP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */
74971473
JG
57 bool is_ipv6;
58 bool eventpoll; /* poll for pcep_event's, or use callback (default) */
59};
60
61bool pcc_active_ = true;
62pcep_session *session = NULL;
63struct cmd_line_args *cmd_line_args = NULL;
64/* pcep_event callback variables */
65bool pcep_event_condition = false;
66struct pcep_event *event = NULL;
67pthread_mutex_t pcep_event_mutex;
68pthread_cond_t pcep_event_cond_var;
69
70static const char DEFAULT_DEST_HOSTNAME[] = "localhost";
71static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost";
72static const short DEFAULT_SRC_TCP_PORT = 4999;
73
74// Private fn's
75struct cmd_line_args *get_cmdline_args(int argc, char *argv[]);
76void handle_signal_action(int sig_number);
77int setup_signals(void);
78void send_pce_path_request_message(pcep_session *session);
79void send_pce_report_message(pcep_session *session);
80void print_queue_event(struct pcep_event *event);
81void pcep_event_callback(void *cb_data, pcep_event *e);
82
83struct cmd_line_args *get_cmdline_args(int argc, char *argv[])
84{
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,
90 MAX_DST_IP_STR);
91 cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT;
92 cmd_line_args->is_ipv6 = false;
93
94 /* Parse the cmd_line args:
95 * -ipv6
96 * -srcip localhost
97 * -destip 192.168.0.2
98 * -srcport 4999
99 * -dstport 4189
100 * -tcpmd5 hello
101 * -event_poll */
102 int i = 1;
103 for (; i < argc; ++i) {
104 if (strcmp(argv[i], "-help") == 0
105 || strcmp(argv[i], "--help") == 0
106 || strcmp(argv[i], "-h") == 0) {
107 pcep_log(
108 LOG_INFO,
109 "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
110 __func__);
111 free(cmd_line_args);
112 return NULL;
113 } else if (strcmp(argv[i], "-ipv6") == 0) {
114 cmd_line_args->is_ipv6 = true;
115 if (argc == 2) {
116 strlcpy(cmd_line_args->dest_ip_str,
117 DEFAULT_DEST_HOSTNAME_IPV6,
118 MAX_DST_IP_STR);
119 }
120 } else if (strcmp(argv[i], "-eventpoll") == 0) {
121 cmd_line_args->eventpoll = true;
122 } else if (strcmp(argv[i], "-srcip") == 0) {
123 if (argc >= i + 2) {
124 strlcpy(cmd_line_args->src_ip_str, argv[++i],
125 MAX_SRC_IP_STR);
126 } else {
127 pcep_log(
128 LOG_ERR,
129 "%s: Invalid number of cmd_line_args for \"-srcip\"",
130 __func__);
131 free(cmd_line_args);
132 return NULL;
133 }
134 } else if (strcmp(argv[i], "-destip") == 0) {
135 if (argc >= i + 2) {
136 strlcpy(cmd_line_args->dest_ip_str, argv[++i],
137 MAX_DST_IP_STR);
138 } else {
139 pcep_log(
140 LOG_ERR,
141 "%s: Invalid number of cmd_line_args for \"-destip\"",
142 __func__);
143 free(cmd_line_args);
144 return NULL;
145 }
146 } else if (strcmp(argv[i], "-srcport") == 0) {
147 if (argc >= i + 2) {
148 cmd_line_args->src_tcp_port = atoi(argv[++i]);
149 } else {
150 pcep_log(
151 LOG_ERR,
152 "%s: Invalid number of cmd_line_args for \"-srcport\"",
153 __func__);
154 free(cmd_line_args);
155 return NULL;
156 }
157 } else if (strcmp(argv[i], "-destport") == 0) {
158 if (argc >= i + 2) {
159 cmd_line_args->dest_tcp_port = atoi(argv[++i]);
160 } else {
161 pcep_log(
162 LOG_ERR,
163 "%s: Invalid number of cmd_line_args for \"-destport\"",
164 __func__);
165 free(cmd_line_args);
166 return NULL;
167 }
168 } else if (strcmp(argv[i], "-tcpmd5") == 0) {
169 if (argc >= i + 2) {
170 strlcpy(cmd_line_args->tcp_md5_str, argv[++i],
171 sizeof(cmd_line_args->tcp_md5_str));
172 } else {
173 pcep_log(
174 LOG_ERR,
175 "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
176 __func__);
177 free(cmd_line_args);
178 return NULL;
179 }
180 } else {
181 pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s",
182 __func__, i, argv[i]);
183 free(cmd_line_args);
184 return NULL;
185 }
186 }
187
188 return cmd_line_args;
189}
190
191void handle_signal_action(int sig_number)
192{
193 if (sig_number == SIGINT) {
194 pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__);
195 pcc_active_ = false;
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);
201 }
202 } else if (sig_number == SIGUSR1) {
203 pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters",
204 __func__);
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",
209 __func__);
210 reset_pcep_session_counters(session);
211 }
212}
213
214
215int setup_signals()
216{
217 struct sigaction sa;
6006b807 218 memset(&sa, 0, sizeof(sa));
74971473
JG
219 sa.sa_handler = handle_signal_action;
220 if (sigaction(SIGINT, &sa, 0) != 0) {
221 perror("sigaction()");
222 return -1;
223 }
224
225 if (sigaction(SIGUSR1, &sa, 0) != 0) {
226 perror("sigaction()");
227 return -1;
228 }
229
230 if (sigaction(SIGUSR2, &sa, 0) != 0) {
231 perror("sigaction()");
232 return -1;
233 }
234
235 return 0;
236}
237
238void send_pce_path_request_message(pcep_session *session)
239{
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);
244
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);
249
250 struct pcep_message *path_request =
251 pcep_msg_create_request(rp_object, ep_object, NULL);
252 send_message(session, path_request, true);
253}
254
255void send_pce_report_message(pcep_session *session)
256{
257 double_linked_list *report_list = dll_initialize();
258
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);
264
265 /*
266 * Create the SRP object
267 */
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);
272 if (obj == NULL) {
273 pcep_log(LOG_WARNING,
274 "%s: send_pce_report_message SRP object was NULL",
275 __func__);
b8cc7b62 276 dll_destroy_with_data(report_list);
74971473
JG
277 return;
278 }
279 dll_append(report_list, obj);
280
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);
287
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,
296 1, NULL);
297 dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
298
299 /*
300 * Create the LSP object
301 */
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,
312 lsp_tlv_list);
313 if (obj == NULL) {
314 pcep_log(LOG_WARNING,
315 "%s: send_pce_report_message LSP object was NULL",
316 __func__);
b8cc7b62 317 dll_destroy_with_data(report_list);
74971473
JG
318 return;
319 }
320 dll_append(report_list, obj);
321
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);
327
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);
331
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",
341 __func__);
342 return;
343 }
344 dll_append(ero_subobj_list, sr_subobj_ipv4node);
345
346 /*
347 * Create the ERO object
348 */
349 obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list);
350 if (obj == NULL) {
351 pcep_log(LOG_WARNING,
352 "%s: send_pce_report_message ERO object was NULL",
353 __func__);
b8cc7b62 354 dll_destroy_with_data(report_list);
74971473
JG
355 return;
356 }
357 dll_append(report_list, obj);
358
359 /*
360 * Create the Metric object
361 */
362 obj = (struct pcep_object_header *)pcep_obj_create_metric(
363 PCEP_METRIC_TE, false, true, 16.0);
364 dll_append(report_list, obj);
365
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);
369}
370
371void print_queue_event(struct pcep_event *event)
372{
373 pcep_log(
374 LOG_INFO,
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);
379
380 if (event->event_type == MESSAGE_RECEIVED) {
381 pcep_log(
382 LOG_INFO, "%s: \t Event message type [%s]", __func__,
383 get_message_type_str(event->message->msg_header->type));
384 }
385}
386
387/* Called by pcep_session_logic when pcep_event's are ready */
388void pcep_event_callback(void *cb_data, pcep_event *e)
389{
390 (void)cb_data;
391 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
392 time(NULL), pthread_self());
393 pthread_mutex_lock(&pcep_event_mutex);
394 event = e;
395 pcep_event_condition = true;
396 pthread_cond_signal(&pcep_event_cond_var);
397 pthread_mutex_unlock(&pcep_event_mutex);
398}
399
400int main(int argc, char **argv)
401{
402 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
403 __func__, time(NULL), pthread_self());
404
405 cmd_line_args = get_cmdline_args(argc, argv);
406 if (cmd_line_args == NULL) {
407 return -1;
408 }
409
410 setup_signals();
411
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)) {
417 pcep_log(LOG_ERR,
418 "%s: Error initializing PCC with infra.",
419 __func__);
420 return -1;
421 }
422 } else {
423 if (!initialize_pcc()) {
424 pcep_log(LOG_ERR, "%s: Error initializing PCC.",
425 __func__);
426 return -1;
427 }
428 }
429
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;
434
435 strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
436 sizeof(config->tcp_authentication_str));
437
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__);
443 return -1;
444 }
445
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);
450 } else {
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);
454 }
455
456 if (session == NULL) {
457 pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
458 destroy_pcep_configuration(config);
459 return -1;
460 }
461
462 sleep(2);
463
464 send_pce_report_message(session);
465 /*send_pce_path_request_message(session);*/
466
467 /* Wait for pcep_event's either by polling the event queue or by
468 * callback */
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);
477 }
478
479 sleep(5);
480 }
481 } else {
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);
487
488 /* this internal loop helps avoid spurious interrupts */
489 while (!pcep_event_condition) {
490 pthread_cond_wait(&pcep_event_cond_var,
491 &pcep_event_mutex);
492 }
493
494 /* Check if we have been interrupted by SIGINT */
495 if (pcc_active_) {
496 print_queue_event(event);
497 destroy_pcep_event(event);
498 }
499
500 pcep_event_condition = false;
501 pthread_mutex_unlock(&pcep_event_mutex);
502 }
503
504 pthread_mutex_destroy(&pcep_event_mutex);
505 pthread_cond_destroy(&pcep_event_cond_var);
506 }
507
508 pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
509 disconnect_pce(session);
510 destroy_pcep_configuration(config);
511 free(cmd_line_args);
512
513 if (!destroy_pcc()) {
514 pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
515 }
516
517 pceplib_memory_dump();
518
519 return 0;
520}