]>
Commit | Line | Data |
---|---|---|
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 | |
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; | |
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 | ||
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); | |
74971473 JG |
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); | |
81 | ||
82 | struct 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 | ||
190 | void 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 | 213 | static 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 | ||
236 | void 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 | ||
253 | void 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 | ||
369 | void 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 */ | |
386 | void 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 | ||
398 | int 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 | } |