]>
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); | |
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__); | |
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 | ||
371 | void 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 */ | |
388 | void 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 | ||
400 | int 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 | } |