]> git.proxmox.com Git - mirror_frr.git/blame - pceplib/pcep_pcc.c
Merge pull request #12114 from opensourcerouting/feature/bgp_aigp_attribute
[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);
74971473
JG
77void send_pce_path_request_message(pcep_session *session);
78void send_pce_report_message(pcep_session *session);
79void print_queue_event(struct pcep_event *event);
80void pcep_event_callback(void *cb_data, pcep_event *e);
81
82struct cmd_line_args *get_cmdline_args(int argc, char *argv[])
83{
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,
89 MAX_DST_IP_STR);
90 cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT;
91 cmd_line_args->is_ipv6 = false;
92
93 /* Parse the cmd_line args:
94 * -ipv6
95 * -srcip localhost
96 * -destip 192.168.0.2
97 * -srcport 4999
98 * -dstport 4189
99 * -tcpmd5 hello
100 * -event_poll */
101 int i = 1;
102 for (; i < argc; ++i) {
103 if (strcmp(argv[i], "-help") == 0
104 || strcmp(argv[i], "--help") == 0
105 || strcmp(argv[i], "-h") == 0) {
106 pcep_log(
107 LOG_INFO,
108 "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
109 __func__);
110 free(cmd_line_args);
111 return NULL;
112 } else if (strcmp(argv[i], "-ipv6") == 0) {
113 cmd_line_args->is_ipv6 = true;
114 if (argc == 2) {
115 strlcpy(cmd_line_args->dest_ip_str,
116 DEFAULT_DEST_HOSTNAME_IPV6,
117 MAX_DST_IP_STR);
118 }
119 } else if (strcmp(argv[i], "-eventpoll") == 0) {
120 cmd_line_args->eventpoll = true;
121 } else if (strcmp(argv[i], "-srcip") == 0) {
122 if (argc >= i + 2) {
123 strlcpy(cmd_line_args->src_ip_str, argv[++i],
124 MAX_SRC_IP_STR);
125 } else {
126 pcep_log(
127 LOG_ERR,
128 "%s: Invalid number of cmd_line_args for \"-srcip\"",
129 __func__);
130 free(cmd_line_args);
131 return NULL;
132 }
133 } else if (strcmp(argv[i], "-destip") == 0) {
134 if (argc >= i + 2) {
135 strlcpy(cmd_line_args->dest_ip_str, argv[++i],
136 MAX_DST_IP_STR);
137 } else {
138 pcep_log(
139 LOG_ERR,
140 "%s: Invalid number of cmd_line_args for \"-destip\"",
141 __func__);
142 free(cmd_line_args);
143 return NULL;
144 }
145 } else if (strcmp(argv[i], "-srcport") == 0) {
146 if (argc >= i + 2) {
147 cmd_line_args->src_tcp_port = atoi(argv[++i]);
148 } else {
149 pcep_log(
150 LOG_ERR,
151 "%s: Invalid number of cmd_line_args for \"-srcport\"",
152 __func__);
153 free(cmd_line_args);
154 return NULL;
155 }
156 } else if (strcmp(argv[i], "-destport") == 0) {
157 if (argc >= i + 2) {
158 cmd_line_args->dest_tcp_port = atoi(argv[++i]);
159 } else {
160 pcep_log(
161 LOG_ERR,
162 "%s: Invalid number of cmd_line_args for \"-destport\"",
163 __func__);
164 free(cmd_line_args);
165 return NULL;
166 }
167 } else if (strcmp(argv[i], "-tcpmd5") == 0) {
168 if (argc >= i + 2) {
169 strlcpy(cmd_line_args->tcp_md5_str, argv[++i],
170 sizeof(cmd_line_args->tcp_md5_str));
171 } else {
172 pcep_log(
173 LOG_ERR,
174 "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
175 __func__);
176 free(cmd_line_args);
177 return NULL;
178 }
179 } else {
180 pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s",
181 __func__, i, argv[i]);
182 free(cmd_line_args);
183 return NULL;
184 }
185 }
186
187 return cmd_line_args;
188}
189
190void handle_signal_action(int sig_number)
191{
192 if (sig_number == SIGINT) {
193 pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__);
194 pcc_active_ = false;
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);
200 }
201 } else if (sig_number == SIGUSR1) {
202 pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters",
203 __func__);
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",
208 __func__);
209 reset_pcep_session_counters(session);
210 }
211}
212
2816045a 213static int setup_signals(void)
74971473
JG
214{
215 struct sigaction sa;
6006b807 216 memset(&sa, 0, sizeof(sa));
74971473
JG
217 sa.sa_handler = handle_signal_action;
218 if (sigaction(SIGINT, &sa, 0) != 0) {
219 perror("sigaction()");
220 return -1;
221 }
222
223 if (sigaction(SIGUSR1, &sa, 0) != 0) {
224 perror("sigaction()");
225 return -1;
226 }
227
228 if (sigaction(SIGUSR2, &sa, 0) != 0) {
229 perror("sigaction()");
230 return -1;
231 }
232
233 return 0;
234}
235
236void send_pce_path_request_message(pcep_session *session)
237{
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);
242
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);
247
248 struct pcep_message *path_request =
249 pcep_msg_create_request(rp_object, ep_object, NULL);
250 send_message(session, path_request, true);
251}
252
253void send_pce_report_message(pcep_session *session)
254{
255 double_linked_list *report_list = dll_initialize();
256
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);
262
263 /*
264 * Create the SRP object
265 */
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);
270 if (obj == NULL) {
271 pcep_log(LOG_WARNING,
272 "%s: send_pce_report_message SRP object was NULL",
273 __func__);
b8cc7b62 274 dll_destroy_with_data(report_list);
74971473
JG
275 return;
276 }
277 dll_append(report_list, obj);
278
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);
285
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,
294 1, NULL);
295 dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
296
297 /*
298 * Create the LSP object
299 */
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,
310 lsp_tlv_list);
311 if (obj == NULL) {
312 pcep_log(LOG_WARNING,
313 "%s: send_pce_report_message LSP object was NULL",
314 __func__);
b8cc7b62 315 dll_destroy_with_data(report_list);
74971473
JG
316 return;
317 }
318 dll_append(report_list, obj);
319
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);
325
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);
329
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",
339 __func__);
340 return;
341 }
342 dll_append(ero_subobj_list, sr_subobj_ipv4node);
343
344 /*
345 * Create the ERO object
346 */
347 obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list);
348 if (obj == NULL) {
349 pcep_log(LOG_WARNING,
350 "%s: send_pce_report_message ERO object was NULL",
351 __func__);
b8cc7b62 352 dll_destroy_with_data(report_list);
74971473
JG
353 return;
354 }
355 dll_append(report_list, obj);
356
357 /*
358 * Create the Metric object
359 */
360 obj = (struct pcep_object_header *)pcep_obj_create_metric(
361 PCEP_METRIC_TE, false, true, 16.0);
362 dll_append(report_list, obj);
363
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);
367}
368
369void print_queue_event(struct pcep_event *event)
370{
371 pcep_log(
372 LOG_INFO,
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);
377
378 if (event->event_type == MESSAGE_RECEIVED) {
379 pcep_log(
380 LOG_INFO, "%s: \t Event message type [%s]", __func__,
381 get_message_type_str(event->message->msg_header->type));
382 }
383}
384
385/* Called by pcep_session_logic when pcep_event's are ready */
386void pcep_event_callback(void *cb_data, pcep_event *e)
387{
388 (void)cb_data;
389 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
390 time(NULL), pthread_self());
391 pthread_mutex_lock(&pcep_event_mutex);
392 event = e;
393 pcep_event_condition = true;
394 pthread_cond_signal(&pcep_event_cond_var);
395 pthread_mutex_unlock(&pcep_event_mutex);
396}
397
398int main(int argc, char **argv)
399{
400 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
401 __func__, time(NULL), pthread_self());
402
403 cmd_line_args = get_cmdline_args(argc, argv);
404 if (cmd_line_args == NULL) {
405 return -1;
406 }
407
408 setup_signals();
409
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)) {
415 pcep_log(LOG_ERR,
416 "%s: Error initializing PCC with infra.",
417 __func__);
418 return -1;
419 }
420 } else {
421 if (!initialize_pcc()) {
422 pcep_log(LOG_ERR, "%s: Error initializing PCC.",
423 __func__);
424 return -1;
425 }
426 }
427
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;
432
433 strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
434 sizeof(config->tcp_authentication_str));
435
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__);
441 return -1;
442 }
443
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);
448 } else {
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);
452 }
453
454 if (session == NULL) {
455 pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
456 destroy_pcep_configuration(config);
457 return -1;
458 }
459
460 sleep(2);
461
462 send_pce_report_message(session);
463 /*send_pce_path_request_message(session);*/
464
465 /* Wait for pcep_event's either by polling the event queue or by
466 * callback */
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);
475 }
476
477 sleep(5);
478 }
479 } else {
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);
485
486 /* this internal loop helps avoid spurious interrupts */
487 while (!pcep_event_condition) {
488 pthread_cond_wait(&pcep_event_cond_var,
489 &pcep_event_mutex);
490 }
491
492 /* Check if we have been interrupted by SIGINT */
493 if (pcc_active_) {
494 print_queue_event(event);
495 destroy_pcep_event(event);
496 }
497
498 pcep_event_condition = false;
499 pthread_mutex_unlock(&pcep_event_mutex);
500 }
501
502 pthread_mutex_destroy(&pcep_event_mutex);
503 pthread_cond_destroy(&pcep_event_cond_var);
504 }
505
506 pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
507 disconnect_pce(session);
508 destroy_pcep_configuration(config);
509 free(cmd_line_args);
510
511 if (!destroy_pcc()) {
512 pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
513 }
514
515 pceplib_memory_dump();
516
517 return 0;
518}