]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_pcc.c
Merge pull request #8255 from pjdruddy/fix-ifindex-test
[mirror_frr.git] / pceplib / pcep_pcc.c
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
51 struct 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;
56 char tcp_md5_str[TCP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */
57 bool is_ipv6;
58 bool eventpoll; /* poll for pcep_event's, or use callback (default) */
59 };
60
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;
69
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;
73
74 // Private fn's
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);
82
83 struct 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
191 void 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
215 int setup_signals()
216 {
217 struct sigaction sa;
218 memset(&sa, 0, sizeof(struct sigaction));
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
238 void 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
255 void 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__);
276 return;
277 }
278 dll_append(report_list, obj);
279
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);
286
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,
295 1, NULL);
296 dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
297
298 /*
299 * Create the LSP object
300 */
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,
311 lsp_tlv_list);
312 if (obj == NULL) {
313 pcep_log(LOG_WARNING,
314 "%s: send_pce_report_message LSP object was NULL",
315 __func__);
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__);
352 return;
353 }
354 dll_append(report_list, obj);
355
356 /*
357 * Create the Metric object
358 */
359 obj = (struct pcep_object_header *)pcep_obj_create_metric(
360 PCEP_METRIC_TE, false, true, 16.0);
361 dll_append(report_list, obj);
362
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);
366 }
367
368 void print_queue_event(struct pcep_event *event)
369 {
370 pcep_log(
371 LOG_INFO,
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);
376
377 if (event->event_type == MESSAGE_RECEIVED) {
378 pcep_log(
379 LOG_INFO, "%s: \t Event message type [%s]", __func__,
380 get_message_type_str(event->message->msg_header->type));
381 }
382 }
383
384 /* Called by pcep_session_logic when pcep_event's are ready */
385 void pcep_event_callback(void *cb_data, pcep_event *e)
386 {
387 (void)cb_data;
388 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
389 time(NULL), pthread_self());
390 pthread_mutex_lock(&pcep_event_mutex);
391 event = e;
392 pcep_event_condition = true;
393 pthread_cond_signal(&pcep_event_cond_var);
394 pthread_mutex_unlock(&pcep_event_mutex);
395 }
396
397 int main(int argc, char **argv)
398 {
399 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
400 __func__, time(NULL), pthread_self());
401
402 cmd_line_args = get_cmdline_args(argc, argv);
403 if (cmd_line_args == NULL) {
404 return -1;
405 }
406
407 setup_signals();
408
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)) {
414 pcep_log(LOG_ERR,
415 "%s: Error initializing PCC with infra.",
416 __func__);
417 return -1;
418 }
419 } else {
420 if (!initialize_pcc()) {
421 pcep_log(LOG_ERR, "%s: Error initializing PCC.",
422 __func__);
423 return -1;
424 }
425 }
426
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;
431
432 strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
433 sizeof(config->tcp_authentication_str));
434
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__);
440 return -1;
441 }
442
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);
447 } else {
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);
451 }
452
453 if (session == NULL) {
454 pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
455 destroy_pcep_configuration(config);
456 return -1;
457 }
458
459 sleep(2);
460
461 send_pce_report_message(session);
462 /*send_pce_path_request_message(session);*/
463
464 /* Wait for pcep_event's either by polling the event queue or by
465 * callback */
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);
474 }
475
476 sleep(5);
477 }
478 } else {
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);
484
485 /* this internal loop helps avoid spurious interrupts */
486 while (!pcep_event_condition) {
487 pthread_cond_wait(&pcep_event_cond_var,
488 &pcep_event_mutex);
489 }
490
491 /* Check if we have been interrupted by SIGINT */
492 if (pcc_active_) {
493 print_queue_event(event);
494 destroy_pcep_event(event);
495 }
496
497 pcep_event_condition = false;
498 pthread_mutex_unlock(&pcep_event_mutex);
499 }
500
501 pthread_mutex_destroy(&pcep_event_mutex);
502 pthread_cond_destroy(&pcep_event_cond_var);
503 }
504
505 pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
506 disconnect_pce(session);
507 destroy_pcep_configuration(config);
508 free(cmd_line_args);
509
510 if (!destroy_pcc()) {
511 pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
512 }
513
514 pceplib_memory_dump();
515
516 return 0;
517 }