]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: LGPL-2.1-or-later |
74971473 JG |
2 | /* |
3 | * This file is part of the PCEPlib, a PCEP protocol library. | |
4 | * | |
5 | * Copyright (C) 2020 Volta Networks https://voltanet.io/ | |
6 | * | |
74971473 JG |
7 | * Author : Brady Johnson <brady@voltanet.io> |
8 | * | |
9 | */ | |
10 | ||
11 | ||
12 | /* | |
13 | * Sample PCC implementation | |
14 | */ | |
15 | ||
16 | #include <zebra.h> | |
17 | ||
18 | #include <netdb.h> // gethostbyname | |
19 | #include <netinet/tcp.h> | |
20 | #include <pthread.h> | |
21 | #include <signal.h> | |
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <time.h> | |
26 | #include <unistd.h> | |
27 | ||
28 | #include "pcep_pcc_api.h" | |
29 | #include "pcep_utils_double_linked_list.h" | |
30 | #include "pcep_utils_logging.h" | |
31 | #include "pcep_utils_memory.h" | |
32 | ||
33 | /* | |
34 | * PCEP PCC design spec: | |
35 | * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing | |
36 | */ | |
37 | #define MAX_SRC_IP_STR 40 | |
38 | #define MAX_DST_IP_STR 40 | |
39 | struct cmd_line_args { | |
40 | char src_ip_str[MAX_SRC_IP_STR]; | |
41 | char dest_ip_str[MAX_DST_IP_STR]; | |
42 | short src_tcp_port; | |
43 | short dest_tcp_port; | |
2a034138 | 44 | char tcp_md5_str[PCEP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */ |
74971473 JG |
45 | bool is_ipv6; |
46 | bool eventpoll; /* poll for pcep_event's, or use callback (default) */ | |
47 | }; | |
48 | ||
49 | bool pcc_active_ = true; | |
50 | pcep_session *session = NULL; | |
51 | struct cmd_line_args *cmd_line_args = NULL; | |
52 | /* pcep_event callback variables */ | |
53 | bool pcep_event_condition = false; | |
54 | struct pcep_event *event = NULL; | |
55 | pthread_mutex_t pcep_event_mutex; | |
56 | pthread_cond_t pcep_event_cond_var; | |
57 | ||
58 | static const char DEFAULT_DEST_HOSTNAME[] = "localhost"; | |
59 | static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost"; | |
60 | static const short DEFAULT_SRC_TCP_PORT = 4999; | |
61 | ||
62 | // Private fn's | |
63 | struct cmd_line_args *get_cmdline_args(int argc, char *argv[]); | |
64 | void handle_signal_action(int sig_number); | |
74971473 JG |
65 | void send_pce_path_request_message(pcep_session *session); |
66 | void send_pce_report_message(pcep_session *session); | |
67 | void print_queue_event(struct pcep_event *event); | |
68 | void pcep_event_callback(void *cb_data, pcep_event *e); | |
69 | ||
70 | struct cmd_line_args *get_cmdline_args(int argc, char *argv[]) | |
71 | { | |
72 | /* Allocate and set default values */ | |
73 | struct cmd_line_args *cmd_line_args = | |
74 | malloc(sizeof(struct cmd_line_args)); | |
75 | memset(cmd_line_args, 0, sizeof(struct cmd_line_args)); | |
76 | strlcpy(cmd_line_args->dest_ip_str, DEFAULT_DEST_HOSTNAME, | |
77 | MAX_DST_IP_STR); | |
78 | cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT; | |
79 | cmd_line_args->is_ipv6 = false; | |
80 | ||
81 | /* Parse the cmd_line args: | |
82 | * -ipv6 | |
83 | * -srcip localhost | |
84 | * -destip 192.168.0.2 | |
85 | * -srcport 4999 | |
86 | * -dstport 4189 | |
87 | * -tcpmd5 hello | |
88 | * -event_poll */ | |
89 | int i = 1; | |
90 | for (; i < argc; ++i) { | |
91 | if (strcmp(argv[i], "-help") == 0 | |
92 | || strcmp(argv[i], "--help") == 0 | |
93 | || strcmp(argv[i], "-h") == 0) { | |
94 | pcep_log( | |
95 | LOG_INFO, | |
96 | "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]", | |
97 | __func__); | |
98 | free(cmd_line_args); | |
99 | return NULL; | |
100 | } else if (strcmp(argv[i], "-ipv6") == 0) { | |
101 | cmd_line_args->is_ipv6 = true; | |
102 | if (argc == 2) { | |
103 | strlcpy(cmd_line_args->dest_ip_str, | |
104 | DEFAULT_DEST_HOSTNAME_IPV6, | |
105 | MAX_DST_IP_STR); | |
106 | } | |
107 | } else if (strcmp(argv[i], "-eventpoll") == 0) { | |
108 | cmd_line_args->eventpoll = true; | |
109 | } else if (strcmp(argv[i], "-srcip") == 0) { | |
110 | if (argc >= i + 2) { | |
111 | strlcpy(cmd_line_args->src_ip_str, argv[++i], | |
112 | MAX_SRC_IP_STR); | |
113 | } else { | |
114 | pcep_log( | |
115 | LOG_ERR, | |
116 | "%s: Invalid number of cmd_line_args for \"-srcip\"", | |
117 | __func__); | |
118 | free(cmd_line_args); | |
119 | return NULL; | |
120 | } | |
121 | } else if (strcmp(argv[i], "-destip") == 0) { | |
122 | if (argc >= i + 2) { | |
123 | strlcpy(cmd_line_args->dest_ip_str, argv[++i], | |
124 | MAX_DST_IP_STR); | |
125 | } else { | |
126 | pcep_log( | |
127 | LOG_ERR, | |
128 | "%s: Invalid number of cmd_line_args for \"-destip\"", | |
129 | __func__); | |
130 | free(cmd_line_args); | |
131 | return NULL; | |
132 | } | |
133 | } else if (strcmp(argv[i], "-srcport") == 0) { | |
134 | if (argc >= i + 2) { | |
135 | cmd_line_args->src_tcp_port = atoi(argv[++i]); | |
136 | } else { | |
137 | pcep_log( | |
138 | LOG_ERR, | |
139 | "%s: Invalid number of cmd_line_args for \"-srcport\"", | |
140 | __func__); | |
141 | free(cmd_line_args); | |
142 | return NULL; | |
143 | } | |
144 | } else if (strcmp(argv[i], "-destport") == 0) { | |
145 | if (argc >= i + 2) { | |
146 | cmd_line_args->dest_tcp_port = atoi(argv[++i]); | |
147 | } else { | |
148 | pcep_log( | |
149 | LOG_ERR, | |
150 | "%s: Invalid number of cmd_line_args for \"-destport\"", | |
151 | __func__); | |
152 | free(cmd_line_args); | |
153 | return NULL; | |
154 | } | |
155 | } else if (strcmp(argv[i], "-tcpmd5") == 0) { | |
156 | if (argc >= i + 2) { | |
157 | strlcpy(cmd_line_args->tcp_md5_str, argv[++i], | |
158 | sizeof(cmd_line_args->tcp_md5_str)); | |
159 | } else { | |
160 | pcep_log( | |
161 | LOG_ERR, | |
162 | "%s: Invalid number of cmd_line_args for \"-tcpmd5\"", | |
163 | __func__); | |
164 | free(cmd_line_args); | |
165 | return NULL; | |
166 | } | |
167 | } else { | |
168 | pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s", | |
169 | __func__, i, argv[i]); | |
170 | free(cmd_line_args); | |
171 | return NULL; | |
172 | } | |
173 | } | |
174 | ||
175 | return cmd_line_args; | |
176 | } | |
177 | ||
178 | void handle_signal_action(int sig_number) | |
179 | { | |
180 | if (sig_number == SIGINT) { | |
181 | pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__); | |
182 | pcc_active_ = false; | |
183 | if (cmd_line_args->eventpoll == false) { | |
184 | pthread_mutex_lock(&pcep_event_mutex); | |
185 | pcep_event_condition = true; | |
186 | pthread_cond_signal(&pcep_event_cond_var); | |
187 | pthread_mutex_unlock(&pcep_event_mutex); | |
188 | } | |
189 | } else if (sig_number == SIGUSR1) { | |
190 | pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters", | |
191 | __func__); | |
192 | dump_pcep_session_counters(session); | |
193 | pceplib_memory_dump(); | |
194 | } else if (sig_number == SIGUSR2) { | |
195 | pcep_log(LOG_INFO, "%s: SIGUSR2 was caught, reseting counters", | |
196 | __func__); | |
197 | reset_pcep_session_counters(session); | |
198 | } | |
199 | } | |
200 | ||
2816045a | 201 | static int setup_signals(void) |
74971473 JG |
202 | { |
203 | struct sigaction sa; | |
6006b807 | 204 | memset(&sa, 0, sizeof(sa)); |
74971473 JG |
205 | sa.sa_handler = handle_signal_action; |
206 | if (sigaction(SIGINT, &sa, 0) != 0) { | |
207 | perror("sigaction()"); | |
208 | return -1; | |
209 | } | |
210 | ||
211 | if (sigaction(SIGUSR1, &sa, 0) != 0) { | |
212 | perror("sigaction()"); | |
213 | return -1; | |
214 | } | |
215 | ||
216 | if (sigaction(SIGUSR2, &sa, 0) != 0) { | |
217 | perror("sigaction()"); | |
218 | return -1; | |
219 | } | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | void send_pce_path_request_message(pcep_session *session) | |
225 | { | |
226 | struct in_addr src_ipv4; | |
227 | struct in_addr dst_ipv4; | |
228 | inet_pton(AF_INET, "1.2.3.4", &src_ipv4); | |
229 | inet_pton(AF_INET, "10.20.30.40", &dst_ipv4); | |
230 | ||
231 | struct pcep_object_rp *rp_object = | |
232 | pcep_obj_create_rp(1, false, false, false, false, 42, NULL); | |
233 | struct pcep_object_endpoints_ipv4 *ep_object = | |
234 | pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4); | |
235 | ||
236 | struct pcep_message *path_request = | |
237 | pcep_msg_create_request(rp_object, ep_object, NULL); | |
238 | send_message(session, path_request, true); | |
239 | } | |
240 | ||
241 | void send_pce_report_message(pcep_session *session) | |
242 | { | |
243 | double_linked_list *report_list = dll_initialize(); | |
244 | ||
245 | /* SRP Path Setup Type TLV */ | |
246 | struct pcep_object_tlv_path_setup_type *pst_tlv = | |
247 | pcep_tlv_create_path_setup_type(SR_TE_PST); | |
248 | double_linked_list *srp_tlv_list = dll_initialize(); | |
249 | dll_append(srp_tlv_list, pst_tlv); | |
250 | ||
251 | /* | |
252 | * Create the SRP object | |
253 | */ | |
254 | uint32_t srp_id_number = 0x10203040; | |
255 | struct pcep_object_header *obj = | |
256 | (struct pcep_object_header *)pcep_obj_create_srp( | |
257 | false, srp_id_number, srp_tlv_list); | |
258 | if (obj == NULL) { | |
259 | pcep_log(LOG_WARNING, | |
260 | "%s: send_pce_report_message SRP object was NULL", | |
261 | __func__); | |
b8cc7b62 | 262 | dll_destroy_with_data(report_list); |
74971473 JG |
263 | return; |
264 | } | |
265 | dll_append(report_list, obj); | |
266 | ||
267 | /* LSP Symbolic path name TLV */ | |
268 | char symbolic_path_name[] = "second-default"; | |
269 | struct pcep_object_tlv_symbolic_path_name *spn_tlv = | |
270 | pcep_tlv_create_symbolic_path_name(symbolic_path_name, 14); | |
271 | double_linked_list *lsp_tlv_list = dll_initialize(); | |
272 | dll_append(lsp_tlv_list, spn_tlv); | |
273 | ||
274 | /* LSP IPv4 LSP ID TLV */ | |
275 | struct in_addr ipv4_tunnel_sender; | |
276 | struct in_addr ipv4_tunnel_endpoint; | |
277 | inet_pton(AF_INET, "9.9.1.1", &ipv4_tunnel_sender); | |
278 | inet_pton(AF_INET, "9.9.2.1", &ipv4_tunnel_endpoint); | |
279 | struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id_tlv = | |
280 | pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender, | |
281 | &ipv4_tunnel_endpoint, 42, | |
282 | 1, NULL); | |
283 | dll_append(lsp_tlv_list, ipv4_lsp_id_tlv); | |
284 | ||
285 | /* | |
286 | * Create the LSP object | |
287 | */ | |
288 | uint32_t plsp_id = 42; | |
289 | enum pcep_lsp_operational_status lsp_status = | |
290 | PCEP_LSP_OPERATIONAL_ACTIVE; | |
291 | bool c_flag = false; /* Lsp was created by PcInitiate msg */ | |
292 | bool a_flag = false; /* Admin state, active / inactive */ | |
293 | bool r_flag = false; /* true if LSP has been removed */ | |
294 | bool s_flag = true; /* Synchronization */ | |
295 | bool d_flag = false; /* Delegate LSP to PCE */ | |
296 | obj = (struct pcep_object_header *)pcep_obj_create_lsp( | |
297 | plsp_id, lsp_status, c_flag, a_flag, r_flag, s_flag, d_flag, | |
298 | lsp_tlv_list); | |
299 | if (obj == NULL) { | |
300 | pcep_log(LOG_WARNING, | |
301 | "%s: send_pce_report_message LSP object was NULL", | |
302 | __func__); | |
b8cc7b62 | 303 | dll_destroy_with_data(report_list); |
74971473 JG |
304 | return; |
305 | } | |
306 | dll_append(report_list, obj); | |
307 | ||
308 | /* Create 2 ERO NONAI sub-objects */ | |
309 | double_linked_list *ero_subobj_list = dll_initialize(); | |
310 | struct pcep_ro_subobj_sr *sr_subobj_nonai1 = | |
311 | pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true); | |
312 | dll_append(ero_subobj_list, sr_subobj_nonai1); | |
313 | ||
314 | struct pcep_ro_subobj_sr *sr_subobj_nonai2 = | |
315 | pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true); | |
316 | dll_append(ero_subobj_list, sr_subobj_nonai2); | |
317 | ||
318 | /* Create ERO IPv4 node sub-object */ | |
319 | struct in_addr sr_subobj_ipv4; | |
320 | inet_pton(AF_INET, "9.9.9.1", &sr_subobj_ipv4); | |
321 | struct pcep_ro_subobj_sr *sr_subobj_ipv4node = | |
322 | pcep_obj_create_ro_subobj_sr_ipv4_node( | |
323 | false, false, false, true, 16060, &sr_subobj_ipv4); | |
324 | if (sr_subobj_ipv4node == NULL) { | |
325 | pcep_log(LOG_WARNING, | |
326 | "%s: send_pce_report_message ERO sub-object was NULL", | |
327 | __func__); | |
328 | return; | |
329 | } | |
330 | dll_append(ero_subobj_list, sr_subobj_ipv4node); | |
331 | ||
332 | /* | |
333 | * Create the ERO object | |
334 | */ | |
335 | obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list); | |
336 | if (obj == NULL) { | |
337 | pcep_log(LOG_WARNING, | |
338 | "%s: send_pce_report_message ERO object was NULL", | |
339 | __func__); | |
b8cc7b62 | 340 | dll_destroy_with_data(report_list); |
74971473 JG |
341 | return; |
342 | } | |
343 | dll_append(report_list, obj); | |
344 | ||
345 | /* | |
346 | * Create the Metric object | |
347 | */ | |
348 | obj = (struct pcep_object_header *)pcep_obj_create_metric( | |
349 | PCEP_METRIC_TE, false, true, 16.0); | |
350 | dll_append(report_list, obj); | |
351 | ||
352 | /* Create and send the report message */ | |
353 | struct pcep_message *report_msg = pcep_msg_create_report(report_list); | |
354 | send_message(session, report_msg, true); | |
355 | } | |
356 | ||
357 | void print_queue_event(struct pcep_event *event) | |
358 | { | |
359 | pcep_log( | |
360 | LOG_INFO, | |
361 | "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]", | |
362 | __func__, time(NULL), pthread_self(), | |
363 | get_event_type_str(event->event_type), | |
364 | event->session->session_id, event->event_time); | |
365 | ||
366 | if (event->event_type == MESSAGE_RECEIVED) { | |
367 | pcep_log( | |
368 | LOG_INFO, "%s: \t Event message type [%s]", __func__, | |
369 | get_message_type_str(event->message->msg_header->type)); | |
370 | } | |
371 | } | |
372 | ||
373 | /* Called by pcep_session_logic when pcep_event's are ready */ | |
374 | void pcep_event_callback(void *cb_data, pcep_event *e) | |
375 | { | |
376 | (void)cb_data; | |
377 | pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__, | |
378 | time(NULL), pthread_self()); | |
379 | pthread_mutex_lock(&pcep_event_mutex); | |
380 | event = e; | |
381 | pcep_event_condition = true; | |
382 | pthread_cond_signal(&pcep_event_cond_var); | |
383 | pthread_mutex_unlock(&pcep_event_mutex); | |
384 | } | |
385 | ||
386 | int main(int argc, char **argv) | |
387 | { | |
388 | pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client", | |
389 | __func__, time(NULL), pthread_self()); | |
390 | ||
391 | cmd_line_args = get_cmdline_args(argc, argv); | |
392 | if (cmd_line_args == NULL) { | |
393 | return -1; | |
394 | } | |
395 | ||
396 | setup_signals(); | |
397 | ||
398 | if (cmd_line_args->eventpoll == false) { | |
399 | struct pceplib_infra_config infra_config; | |
400 | memset(&infra_config, 0, sizeof(infra_config)); | |
401 | infra_config.pcep_event_func = pcep_event_callback; | |
402 | if (!initialize_pcc_infra(&infra_config)) { | |
403 | pcep_log(LOG_ERR, | |
404 | "%s: Error initializing PCC with infra.", | |
405 | __func__); | |
406 | return -1; | |
407 | } | |
408 | } else { | |
409 | if (!initialize_pcc()) { | |
410 | pcep_log(LOG_ERR, "%s: Error initializing PCC.", | |
411 | __func__); | |
412 | return -1; | |
413 | } | |
414 | } | |
415 | ||
416 | pcep_configuration *config = create_default_pcep_configuration(); | |
417 | config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true; | |
418 | config->src_pcep_port = cmd_line_args->src_tcp_port; | |
419 | config->is_tcp_auth_md5 = true; | |
420 | ||
421 | strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str, | |
422 | sizeof(config->tcp_authentication_str)); | |
423 | ||
424 | int af = (cmd_line_args->is_ipv6 ? AF_INET6 : AF_INET); | |
425 | struct hostent *host_info = | |
426 | gethostbyname2(cmd_line_args->dest_ip_str, af); | |
427 | if (host_info == NULL) { | |
428 | pcep_log(LOG_ERR, "%s: Error getting IP address.", __func__); | |
429 | return -1; | |
430 | } | |
431 | ||
432 | if (cmd_line_args->is_ipv6) { | |
433 | struct in6_addr host_address; | |
434 | memcpy(&host_address, host_info->h_addr, host_info->h_length); | |
435 | session = connect_pce_ipv6(config, &host_address); | |
436 | } else { | |
437 | struct in_addr host_address; | |
438 | memcpy(&host_address, host_info->h_addr, host_info->h_length); | |
439 | session = connect_pce(config, &host_address); | |
440 | } | |
441 | ||
442 | if (session == NULL) { | |
443 | pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__); | |
444 | destroy_pcep_configuration(config); | |
445 | return -1; | |
446 | } | |
447 | ||
448 | sleep(2); | |
449 | ||
450 | send_pce_report_message(session); | |
451 | /*send_pce_path_request_message(session);*/ | |
452 | ||
453 | /* Wait for pcep_event's either by polling the event queue or by | |
454 | * callback */ | |
455 | if (cmd_line_args->eventpoll == true) { | |
456 | /* Poll the pcep_event queue*/ | |
457 | while (pcc_active_) { | |
458 | if (event_queue_is_empty() == false) { | |
459 | struct pcep_event *event = | |
460 | event_queue_get_event(); | |
461 | print_queue_event(event); | |
462 | destroy_pcep_event(event); | |
463 | } | |
464 | ||
465 | sleep(5); | |
466 | } | |
467 | } else { | |
468 | /* Get events via callback and conditional variable */ | |
469 | pthread_mutex_init(&pcep_event_mutex, NULL); | |
470 | pthread_cond_init(&pcep_event_cond_var, NULL); | |
471 | while (pcc_active_) { | |
472 | pthread_mutex_lock(&pcep_event_mutex); | |
473 | ||
474 | /* this internal loop helps avoid spurious interrupts */ | |
475 | while (!pcep_event_condition) { | |
476 | pthread_cond_wait(&pcep_event_cond_var, | |
477 | &pcep_event_mutex); | |
478 | } | |
479 | ||
480 | /* Check if we have been interrupted by SIGINT */ | |
481 | if (pcc_active_) { | |
482 | print_queue_event(event); | |
483 | destroy_pcep_event(event); | |
484 | } | |
485 | ||
486 | pcep_event_condition = false; | |
487 | pthread_mutex_unlock(&pcep_event_mutex); | |
488 | } | |
489 | ||
490 | pthread_mutex_destroy(&pcep_event_mutex); | |
491 | pthread_cond_destroy(&pcep_event_cond_var); | |
492 | } | |
493 | ||
494 | pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__); | |
495 | disconnect_pce(session); | |
496 | destroy_pcep_configuration(config); | |
497 | free(cmd_line_args); | |
498 | ||
499 | if (!destroy_pcc()) { | |
500 | pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__); | |
501 | } | |
502 | ||
503 | pceplib_memory_dump(); | |
504 | ||
505 | return 0; | |
506 | } |