diff options
31 files changed, 10762 insertions, 13 deletions
diff --git a/configure.ac b/configure.ac index 168a9041d..0cbf4d22e 100755 --- a/configure.ac +++ b/configure.ac @@ -627,6 +627,8 @@ AC_ARG_ENABLE([pcreposix], AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) AC_ARG_ENABLE([fpm], AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support])) +AC_ARG_ENABLE([pcep], + AS_HELP_STRING([--enable-pcep], [enable PCEP support for pathd])) AC_ARG_ENABLE([systemd], AS_HELP_STRING([--enable-systemd], [enable Systemd support])) AC_ARG_ENABLE([werror], @@ -1681,6 +1683,10 @@ AS_IF([test "$enable_pathd" != "no"], [ AC_DEFINE([HAVE_PATHD], [1], [pathd]) ]) +AS_IF([test "$enable_pcep" != "no"], [ + AC_DEFINE([HAVE_PATHD_PCEP], [1], [pathd-pcep]) +]) + if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then AC_MSG_ERROR(["you must use json-c library to use bfdd"]) fi @@ -2473,6 +2479,18 @@ AM_CONDITIONAL([IRDP], [$IRDP]) AM_CONDITIONAL([FPM], [test "$enable_fpm" = "yes"]) AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"]) AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3]) + +dnl PCEP plugin +AM_CONDITIONAL([HAVE_PATHD_PCEP], [test "$enable_pcep" = "yes"]) +AS_IF([test "$enable_pcep" = "yes"], [ + AC_CHECK_LIB([pcep_pcc], [initialize_pcc], [ + PATHD_PCEP_LIBS="-lpcep_pcc" + ],[ + AC_MSG_ERROR([PCEP library libpcep_pcc not found]) + ]) + AC_SUBST([PATHD_PCEP_LIBS]) +]) + dnl daemons AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"]) AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"]) diff --git a/doc/developer/path-internals-pcep.rst b/doc/developer/path-internals-pcep.rst new file mode 100644 index 000000000..ca318314f --- /dev/null +++ b/doc/developer/path-internals-pcep.rst @@ -0,0 +1,193 @@ +PCEP Module Internals +===================== + +Introduction +------------ + +The PCEP module for the pathd daemon implements the PCEP protocol described in +:rfc:`5440` to update the policies and candidate paths. + +The protocol encoding/decoding and the basic session management is handled by +the `pceplib external library 1.2 <https://github.com/volta-networks/pceplib/tree/devel-1.2>`_. + +Together with pceplib, this module supports at least partially: + + - :rfc:`5440` + + Most of the protocol defined in the RFC is implemented. + All the messages can be parsed, but this was only tested in the context + of segment routing. Only a very small subset of metric types can be + configured, and there is a known issue with some Cisco routers not + following the IANA numbers for metrics. + + - :rfc:`8231` + + Support delegation of candidate path after performing the initial + computation request. If the PCE does not respond or cannot compute + a path, an empty candidate path is delegated to the PCE. + Only tested in the context of segment routing. + + - :rfc:`8408` + + Only used to comunicate the support for segment routing to the PCE. + + - :rfc:`8664` + + All the NAI types are implemented, but only the MPLS NAI are supported. + If the PCE provide segments that are not MPLS labels, the PCC will + return an error. + +Note that pceplib supports more RFCs and drafts, see pceplib +`README <https://github.com/volta-networks/pceplib/blob/master/README.md>`_ +for more details. + + +Architecture +------------ + +Overview +........ + +The module is separated into multiple layers: + + - pathd interface + - command-line console + - controller + - PCC + - pceplib interface + +The pathd interface handles all the interactions with the daemon API. + +The command-line console handles all the VTYSH configuration commands. + +The controller manages the multiple PCC connections and the interaction between +them and the daemon interface. + +The PCC handles a single connection to a PCE through a pceplib session. + +The pceplib interface abstracts the API of the pceplib. + +.. figure:: ../figures/pcep_module_threading_overview.svg + + +Threading Model +--------------- + +The module requires multiple threads to cooperate: + + - The main thread used by the pathd daemon. + - The controller pthread used to isolate the PCC from the main thread. + - The possible threads started in the pceplib library. + +To ensure thread safety, all the controller and PCC state data structures can +only be read and modified in the controller thread, and all the global data +structures can only be read and modified in the main thread. Most of the +interactions between these threads are done through FRR timers and events. + +The controller is the bridge between the two threads, all the functions that +**MUST** be called from the main thread start with the prefix `pcep_ctrl_` and +all the functions that **MUST** be called from the controller thread start +with the prefix `pcep_thread_`. When an asynchronous action must be taken in +a different thread, an FRR event is sent to the thread. If some synchronous +operation is needed, the calling thread will block and run a callback in the +other thread, there the result is **COPIED** and returned to the calling thread. + +No function other than the controller functions defined for it should be called +from the main thread. The only exception being some utility functions from +`path_pcep_lib.[hc]`. + +All the calls to pathd API functions **MUST** be performed in the main thread, +for that, the controller sends FRR events handled in function +`path_pcep.c:pcep_main_event_handler`. + +For the same reason, the console client only runs in the main thread. It can +freely use the global variable, but **MUST** use controller's `pcep_ctrl_` +functions to interact with the PCCs. + + +Source Code +----------- + +Generic Data Structures +....................... + +The data structures are defined in multiple places, and where they are defined +dictates where they can be used. + +The data structures defined in `path_pcep.h` can be used anywhere in the module. + +Internally, throughout the module, the `struct path` data structure is used +to describe PCEP messages. It is a simplified flattened structure that can +represent multiple complex PCEP message types. The conversion from this +structure to the PCEP data structures used by pceplib is done in the pceplib +interface layer. + +The data structures defined in `path_pcep_controller.h` should only be used +in `path_pcep_controller.c`. Even if a structure pointer is passed as a parameter +to functions defined in `path_pcep_pcc.h`, these should consider it as an opaque +data structure only used to call back controller functions. + +The same applies to the structures defined in `path_pcep_pcc.h`, even if the +controller owns a reference to this data structure, it should never read or +modify it directly, it should be considered an opaque structure. + +The global data structure can be accessed from the pathd interface layer +`path_pcep.c` and the command line client code `path_pcep_cli.c`. + + +Interface With Pathd +.................... + +All the functions calling or called by the pathd daemon are implemented in +`path_pcep.c`. These functions **MUST** run in the main FRR thread, and +all the interactions with the controller and the PCCs **MUST** pass through +the controller's `pcep_ctrl_` prefixed functions. + +To handle asynchronous events from the PCCs, a callback is passed to +`pcep_ctrl_initialize` that is called in the FRR main thread context. + + +Command Line Client +................... + +All the command line configuration commands (VTYSH) are implemented in +`path_pcep_cli.c`. All the functions there run in the main FRR thread and +can freely access the global variables. All the interaction with the +controller's and the PCCs **MUST** pass through the controller `pcep_ctrl_` +prefixed functions. + + +Debugging Helpers +................. + +All the functions formating data structures for debugging and logging purposes +are implemented in `path_pcep_debug.[hc]`. + + +Interface with pceplib +...................... + +All the functions calling the pceplib external library are defined in +`path_pcep_lib.[hc]`. Some functions are called from the main FRR thread, like +`pcep_lib_initialize`, `pcep_lib_finalize`; some can be called from either +thread, like `pcep_lib_free_counters`; some function must be called from the +controller thread, like `pcep_lib_connect`. This will probably be formalized +later on with function prefix like done in the controller. + + +Controller +.......... + +The controller is defined and implemented in `path_pcep_controller.[hc]`. +Part of the controller code runs in FRR main thread and part runs in its own +FRR pthread started to isolate the main thread from the PCCs' event loop. +To communicate between the threads it uses FRR events, timers and +`thread_execute` calls. + + +PCC +... + +Each PCC instance owns its state and runs in the controller thread. They are +defined and implemented in `path_pcep_pcc.[hc]`. All the interactions with +the daemon must pass through some controller's `pcep_thread_` prefixed function. diff --git a/doc/developer/path-internals.rst b/doc/developer/path-internals.rst index 980d35945..2c2df0f37 100644 --- a/doc/developer/path-internals.rst +++ b/doc/developer/path-internals.rst @@ -8,3 +8,4 @@ Internals :maxdepth: 2 path-internals-daemon + path-internals-pcep diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 400555a2e..f131ba89d 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -47,6 +47,7 @@ dev_RSTFILES = \ doc/developer/packaging-redhat.rst \ doc/developer/packaging.rst \ doc/developer/path-internals-daemon.rst \ + doc/developer/path-internals-pcep.rst \ doc/developer/path-internals.rst \ doc/developer/path.rst \ doc/developer/rcu.rst \ diff --git a/doc/figures/pcep_module_threading_overview.svg b/doc/figures/pcep_module_threading_overview.svg new file mode 100644 index 000000000..4d2d2a254 --- /dev/null +++ b/doc/figures/pcep_module_threading_overview.svg @@ -0,0 +1,481 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.2" width="210mm" height="297mm" viewBox="0 0 21000 29700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve"> + <defs class="ClipPathGroup"> + <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"> + <rect x="0" y="0" width="21000" height="29700"/> + </clipPath> + <clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse"> + <rect x="21" y="29" width="20958" height="29641"/> + </clipPath> + </defs> + <defs> + <font id="EmbeddedFont_1" horiz-adv-x="2048"> + <font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/> + <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/> + <glyph unicode="u" horiz-adv-x="874" d="M 314,1082 L 314,396 C 314,325 321,269 335,230 349,191 371,162 402,145 433,128 478,119 537,119 624,119 692,149 742,208 792,267 817,350 817,455 L 817,1082 997,1082 997,231 C 997,105 999,28 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 826,132 825,185 L 822,185 C 781,110 733,58 679,27 624,-4 557,-20 476,-20 357,-20 271,10 216,69 161,128 133,225 133,361 L 133,1082 314,1082 Z"/> + <glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/> + <glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/> + <glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/> + <glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/> + <glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/> + <glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/> + <glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/> + <glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 356,968 402,1020 457,1053 511,1086 580,1102 663,1102 780,1102 867,1073 923,1015 978,956 1006,858 1006,721 L 1006,0 825,0 825,686 C 825,762 818,819 804,856 790,893 767,920 735,937 703,954 659,963 602,963 517,963 450,934 399,875 348,816 322,737 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1057 321,1015 319,972 316,929 315,904 314,897 L 317,897 Z"/> + <glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/> + <glyph unicode="d" horiz-adv-x="927" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 821,1035 821,1484 1001,1484 1001,223 C 1001,110 1003,36 1007,0 L 835,0 C 833,11 831,35 829,74 826,113 825,146 825,174 L 821,174 Z M 275,542 C 275,391 295,282 335,217 375,152 440,119 530,119 632,119 706,154 752,225 798,296 821,405 821,554 821,697 798,802 752,869 706,936 633,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/> + <glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/> + <glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,147 315,116 312,74 309,31 307,7 306,0 L 132,0 C 136,36 138,110 138,223 L 138,1484 318,1484 318,1061 C 318,1018 317,967 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,691 844,800 804,865 764,930 699,963 609,963 508,963 434,928 388,859 341,790 318,680 318,529 318,387 341,282 386,215 431,147 505,113 607,113 698,113 763,147 804,214 844,281 864,389 864,540 Z"/> + <glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/> + <glyph unicode="_" horiz-adv-x="1218" d="M -31,-407 L -31,-277 1162,-277 1162,-407 -31,-407 Z"/> + <glyph unicode="Y" horiz-adv-x="1298" d="M 777,584 L 777,0 587,0 587,584 45,1409 255,1409 684,738 1111,1409 1321,1409 777,584 Z"/> + <glyph unicode="X" horiz-adv-x="1298" d="M 1112,0 L 689,616 257,0 46,0 582,732 87,1409 298,1409 690,856 1071,1409 1282,1409 800,739 1323,0 1112,0 Z"/> + <glyph unicode="V" horiz-adv-x="1377" d="M 782,0 L 584,0 9,1409 210,1409 600,417 684,168 768,417 1156,1409 1357,1409 782,0 Z"/> + <glyph unicode="T" horiz-adv-x="1192" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z"/> + <glyph unicode="S" horiz-adv-x="1192" d="M 1272,389 C 1272,259 1221,158 1120,87 1018,16 875,-20 690,-20 347,-20 148,99 93,338 L 278,375 C 299,290 345,228 414,189 483,149 578,129 697,129 820,129 916,150 983,193 1050,235 1083,297 1083,379 1083,425 1073,462 1052,491 1031,520 1001,543 963,562 925,581 880,596 827,609 774,622 716,635 652,650 541,675 456,699 399,724 341,749 295,776 262,807 229,837 203,872 186,913 168,954 159,1000 159,1053 159,1174 205,1267 298,1332 390,1397 522,1430 694,1430 854,1430 976,1406 1061,1357 1146,1308 1205,1224 1239,1106 L 1051,1073 C 1030,1148 991,1202 933,1236 875,1269 795,1286 692,1286 579,1286 493,1267 434,1230 375,1193 345,1137 345,1063 345,1020 357,984 380,956 403,927 436,903 479,884 522,864 609,840 738,811 781,801 825,791 868,781 911,770 952,758 991,744 1030,729 1067,712 1102,693 1136,674 1166,650 1191,622 1216,594 1236,561 1251,523 1265,485 1272,440 1272,389 Z"/> + <glyph unicode="R" horiz-adv-x="1244" d="M 1164,0 L 798,585 359,585 359,0 168,0 168,1409 831,1409 C 990,1409 1112,1374 1199,1303 1285,1232 1328,1133 1328,1006 1328,901 1298,813 1237,742 1176,671 1091,626 984,607 L 1384,0 1164,0 Z M 1136,1004 C 1136,1086 1108,1149 1053,1192 997,1235 917,1256 812,1256 L 359,1256 359,736 820,736 C 921,736 999,760 1054,807 1109,854 1136,919 1136,1004 Z"/> + <glyph unicode="P" horiz-adv-x="1112" d="M 1258,985 C 1258,852 1215,746 1128,667 1041,588 922,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 919,1409 1041,1372 1128,1298 1215,1224 1258,1120 1258,985 Z M 1066,983 C 1066,1165 957,1256 738,1256 L 359,1256 359,700 746,700 C 959,700 1066,794 1066,983 Z"/> + <glyph unicode="O" horiz-adv-x="1430" d="M 1495,711 C 1495,564 1467,435 1411,324 1354,213 1273,128 1168,69 1063,10 938,-20 795,-20 650,-20 526,9 421,68 316,127 235,212 180,323 125,434 97,563 97,711 97,936 159,1113 282,1240 405,1367 577,1430 797,1430 940,1430 1065,1402 1170,1345 1275,1288 1356,1205 1412,1096 1467,987 1495,859 1495,711 Z M 1300,711 C 1300,886 1256,1024 1169,1124 1081,1224 957,1274 797,1274 636,1274 511,1225 423,1126 335,1027 291,889 291,711 291,534 336,394 425,291 514,187 637,135 795,135 958,135 1083,185 1170,286 1257,386 1300,528 1300,711 Z"/> + <glyph unicode="N" horiz-adv-x="1165" d="M 1082,0 L 328,1200 333,1103 338,936 338,0 168,0 168,1409 390,1409 1152,201 C 1144,332 1140,426 1140,485 L 1140,1409 1312,1409 1312,0 1082,0 Z"/> + <glyph unicode="M" horiz-adv-x="1377" d="M 1366,0 L 1366,940 C 1366,1044 1369,1144 1375,1240 1342,1121 1313,1027 1287,960 L 923,0 789,0 420,960 364,1130 331,1240 334,1129 338,940 338,0 168,0 168,1409 419,1409 794,432 C 807,393 820,351 833,306 845,261 853,228 857,208 862,235 874,275 891,330 908,384 919,418 925,432 L 1293,1409 1538,1409 1538,0 1366,0 Z"/> + <glyph unicode="L" horiz-adv-x="927" d="M 168,0 L 168,1409 359,1409 359,156 1071,156 1071,0 168,0 Z"/> + <glyph unicode="I" horiz-adv-x="213" d="M 189,0 L 189,1409 380,1409 380,0 189,0 Z"/> + <glyph unicode="H" horiz-adv-x="1165" d="M 1121,0 L 1121,653 359,653 359,0 168,0 168,1409 359,1409 359,813 1121,813 1121,1409 1312,1409 1312,0 1121,0 Z"/> + <glyph unicode="F" horiz-adv-x="1006" d="M 359,1253 L 359,729 1145,729 1145,571 359,571 359,0 168,0 168,1409 1169,1409 1169,1253 359,1253 Z"/> + <glyph unicode="E" horiz-adv-x="1138" d="M 168,0 L 168,1409 1237,1409 1237,1253 359,1253 359,801 1177,801 1177,647 359,647 359,156 1278,156 1278,0 168,0 Z"/> + <glyph unicode="D" horiz-adv-x="1218" d="M 1381,719 C 1381,574 1353,447 1296,338 1239,229 1159,145 1055,87 951,29 831,0 695,0 L 168,0 168,1409 634,1409 C 873,1409 1057,1349 1187,1230 1316,1110 1381,940 1381,719 Z M 1189,719 C 1189,894 1141,1027 1046,1119 950,1210 811,1256 630,1256 L 359,1256 359,153 673,153 C 776,153 867,176 946,221 1024,266 1084,332 1126,417 1168,502 1189,603 1189,719 Z"/> + <glyph unicode="C" horiz-adv-x="1324" d="M 792,1274 C 636,1274 515,1224 428,1124 341,1023 298,886 298,711 298,538 343,400 434,295 524,190 646,137 800,137 997,137 1146,235 1245,430 L 1401,352 C 1343,231 1262,138 1157,75 1052,12 930,-20 791,-20 649,-20 526,10 423,69 319,128 240,212 186,322 131,431 104,561 104,711 104,936 165,1112 286,1239 407,1366 575,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1174,1103 1122,1166 1050,1209 977,1252 891,1274 792,1274 Z"/> + <glyph unicode="B" horiz-adv-x="1112" d="M 1258,397 C 1258,272 1212,174 1121,105 1030,35 903,0 740,0 L 168,0 168,1409 680,1409 C 1011,1409 1176,1295 1176,1067 1176,984 1153,914 1106,857 1059,800 993,762 908,743 1020,730 1106,692 1167,631 1228,569 1258,491 1258,397 Z M 984,1044 C 984,1120 958,1174 906,1207 854,1240 779,1256 680,1256 L 359,1256 359,810 680,810 C 782,810 858,829 909,868 959,906 984,965 984,1044 Z M 1065,412 C 1065,578 948,661 715,661 L 359,661 359,153 730,153 C 847,153 932,175 985,218 1038,261 1065,326 1065,412 Z"/> + <glyph unicode="A" horiz-adv-x="1377" d="M 1167,0 L 1006,412 364,412 202,0 4,0 579,1409 796,1409 1362,0 1167,0 Z M 685,1265 L 676,1237 C 659,1182 635,1111 602,1024 L 422,561 949,561 768,1026 C 749,1072 731,1124 712,1182 L 685,1265 Z"/> + <glyph unicode=" " horiz-adv-x="556"/> + </font> + </defs> + <defs class="TextShapeIndex"> + <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28"/> + </defs> + <defs class="EmbeddedBulletChars"> + <g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/> + </g> + <g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/> + </g> + <g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/> + </g> + <g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/> + </g> + <g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/> + </g> + <g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/> + </g> + <g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/> + </g> + <g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/> + </g> + <g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/> + </g> + <g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)"> + <path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/> + </g> + </defs> + <defs class="TextEmbeddedBitmaps"/> + <g> + <g id="id2" class="Master_Slide"> + <g id="bg-id2" class="Background"/> + <g id="bo-id2" class="BackgroundObjects"/> + </g> + </g> + <g class="SlideGroup"> + <g> + <g id="container-id1"> + <g id="id1" class="Slide" clip-path="url(#presentation_clip_path)"> + <g class="Page"> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id3"> + <rect class="BoundingBox" stroke="none" fill="none" x="9227" y="2229" width="7040" height="8959"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12771,11160 L 12747,11160 12642,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12578,11160 L 12474,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12410,11160 L 12306,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12242,11160 L 12138,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12074,11160 L 11970,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11906,11160 L 11802,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11738,11160 L 11634,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11570,11160 L 11466,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11402,11160 L 11298,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11234,11160 L 11130,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11066,11160 L 10962,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10898,11160 L 10794,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10730,11160 L 10626,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10562,11160 L 10458,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10394,11160 L 10290,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10226,11160 L 10122,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10058,11160 L 9954,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9890,11160 L 9786,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9722,11160 L 9618,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9554,11160 L 9450,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9386,11160 L 9282,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,11124 L 9254,11020"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10956 L 9254,10852"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10788 L 9254,10684"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10620 L 9254,10516"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10452 L 9254,10348"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10284 L 9254,10180"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,10116 L 9254,10012"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9948 L 9254,9844"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9780 L 9254,9676"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9612 L 9254,9508"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9444 L 9254,9340"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9276 L 9254,9172"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,9108 L 9254,9004"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8940 L 9254,8836"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8772 L 9254,8668"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8604 L 9254,8500"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8436 L 9254,8332"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8268 L 9254,8164"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,8100 L 9254,7996"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7932 L 9254,7828"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7764 L 9254,7660"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7596 L 9254,7492"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7428 L 9254,7324"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7260 L 9254,7156"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,7092 L 9254,6988"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6924 L 9254,6820"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6756 L 9254,6652"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6588 L 9254,6484"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6420 L 9254,6316"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6252 L 9254,6148"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,6084 L 9254,5980"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5916 L 9254,5812"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5748 L 9254,5644"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5580 L 9254,5476"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5412 L 9254,5308"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5244 L 9254,5140"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,5076 L 9254,4972"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4908 L 9254,4804"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4740 L 9254,4636"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4572 L 9254,4467"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4404 L 9254,4299"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4236 L 9254,4131"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,4068 L 9254,3963"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3900 L 9254,3795"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3732 L 9254,3627"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3564 L 9254,3459"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3396 L 9254,3291"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3228 L 9254,3123"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,3060 L 9254,2955"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,2892 L 9254,2787"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,2724 L 9254,2619"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,2556 L 9254,2451"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9254,2388 L 9254,2283"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9290,2256 L 9395,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9458,2256 L 9563,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9626,2256 L 9731,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9794,2256 L 9899,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 9962,2256 L 10067,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10130,2256 L 10235,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10298,2256 L 10403,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10466,2256 L 10571,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10634,2256 L 10739,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10802,2256 L 10907,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 10970,2256 L 11075,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11138,2256 L 11243,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11306,2256 L 11411,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11474,2256 L 11579,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11642,2256 L 11747,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11810,2256 L 11915,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 11978,2256 L 12083,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12146,2256 L 12251,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12314,2256 L 12419,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12482,2256 L 12587,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12650,2256 L 12755,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12818,2256 L 12923,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12986,2256 L 13091,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13154,2256 L 13259,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13322,2256 L 13427,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13490,2256 L 13595,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13659,2256 L 13763,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13827,2256 L 13931,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13995,2256 L 14099,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14163,2256 L 14267,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14331,2256 L 14435,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14499,2256 L 14603,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14667,2256 L 14771,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14835,2256 L 14939,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15003,2256 L 15107,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15171,2256 L 15275,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15339,2256 L 15443,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15507,2256 L 15611,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15675,2256 L 15779,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15843,2256 L 15947,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16011,2256 L 16115,2256"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16179,2256 L 16239,2256 16239,2300"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,2364 L 16239,2468"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,2532 L 16239,2636"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,2700 L 16239,2804"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,2868 L 16239,2972"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3036 L 16239,3140"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3204 L 16239,3308"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3372 L 16239,3476"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3540 L 16239,3644"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3708 L 16239,3812"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,3876 L 16239,3980"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4044 L 16239,4148"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4212 L 16239,4316"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4380 L 16239,4484"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4548 L 16239,4652"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4716 L 16239,4820"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,4884 L 16239,4988"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5052 L 16239,5156"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5220 L 16239,5324"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5388 L 16239,5492"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5556 L 16239,5660"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5724 L 16239,5828"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,5892 L 16239,5996"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6060 L 16239,6164"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6228 L 16239,6332"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6396 L 16239,6500"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6564 L 16239,6668"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6732 L 16239,6836"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,6900 L 16239,7004"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7068 L 16239,7172"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7236 L 16239,7340"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7404 L 16239,7508"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7572 L 16239,7676"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7740 L 16239,7844"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,7908 L 16239,8012"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8076 L 16239,8180"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8244 L 16239,8348"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8412 L 16239,8516"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8580 L 16239,8684"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8748 L 16239,8852"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,8916 L 16239,9020"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9084 L 16239,9188"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9252 L 16239,9356"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9420 L 16239,9524"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9588 L 16239,9693"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9756 L 16239,9861"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,9924 L 16239,10029"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10092 L 16239,10197"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10260 L 16239,10365"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10428 L 16239,10533"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10596 L 16239,10701"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10764 L 16239,10869"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,10932 L 16239,11037"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16239,11100 L 16239,11160 16194,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16131,11160 L 16026,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15963,11160 L 15858,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15795,11160 L 15690,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15627,11160 L 15522,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15459,11160 L 15354,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15291,11160 L 15186,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15123,11160 L 15018,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14955,11160 L 14850,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14787,11160 L 14682,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14619,11160 L 14514,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14451,11160 L 14346,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14283,11160 L 14178,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14115,11160 L 14010,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13947,11160 L 13842,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13779,11160 L 13674,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13611,11160 L 13506,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13443,11160 L 13338,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13275,11160 L 13170,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13107,11160 L 13002,11160"/> + <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 12939,11160 L 12834,11160"/> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id4"> + <rect class="BoundingBox" stroke="none" fill="none" x="3128" y="2360" width="5723" height="840"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="529px" font-weight="400"><tspan class="TextPosition" x="3378" y="2963"><tspan fill="rgb(0,102,179)" stroke="none">MAIN PTHREAD</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id5"> + <rect class="BoundingBox" stroke="none" fill="none" x="9640" y="2360" width="8013" height="840"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="529px" font-weight="400"><tspan class="TextPosition" x="9890" y="2963"><tspan fill="rgb(0,102,179)" stroke="none">CONTROLLER PTHREAD</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id6"> + <rect class="BoundingBox" stroke="none" fill="none" x="3315" y="5190" width="2797" height="1273"/> + <path fill="rgb(114,159,207)" stroke="none" d="M 4713,6461 L 3316,6461 3316,5191 6110,5191 6110,6461 4713,6461 Z"/> + <path fill="none" stroke="rgb(52,101,164)" d="M 4713,6461 L 3316,6461 3316,5191 6110,5191 6110,6461 4713,6461 Z"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="4358" y="5985"><tspan fill="rgb(0,0,0)" stroke="none">CLI</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id7"> + <rect class="BoundingBox" stroke="none" fill="none" x="3315" y="7349" width="2797" height="1273"/> + <path fill="rgb(114,159,207)" stroke="none" d="M 4713,8620 L 3316,8620 3316,7350 6110,7350 6110,8620 4713,8620 Z"/> + <path fill="none" stroke="rgb(52,101,164)" d="M 4713,8620 L 3316,8620 3316,7350 6110,7350 6110,8620 4713,8620 Z"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="3972" y="7888"><tspan fill="rgb(0,0,0)" stroke="none">PATHD</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="3430" y="8400"><tspan fill="rgb(0,0,0)" stroke="none">INTERFACE</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id8"> + <rect class="BoundingBox" stroke="none" fill="none" x="12459" y="6079" width="2797" height="1273"/> + <path fill="rgb(114,159,207)" stroke="none" d="M 13857,7350 L 12460,7350 12460,6080 15254,6080 15254,7350 13857,7350 Z"/> + <path fill="none" stroke="rgb(52,101,164)" d="M 13857,7350 L 12460,7350 12460,6080 15254,6080 15254,7350 13857,7350 Z"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="13375" y="6874"><tspan fill="rgb(0,0,0)" stroke="none">PCC</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id9"> + <rect class="BoundingBox" stroke="none" fill="none" x="1665" y="7578" width="2033" height="205"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 1956,7680 L 3406,7680"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 1665,7680 L 1970,7782 1970,7579 1665,7680 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 3697,7680 L 3393,7579 3393,7782 3697,7680 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id10"> + <rect class="BoundingBox" stroke="none" fill="none" x="1892" y="5137" width="1451" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="2142" y="5520"><tspan fill="rgb(0,0,0)" stroke="none">VTYSH</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id11"> + <rect class="BoundingBox" stroke="none" fill="none" x="1568" y="5738" width="1976" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="1818" y="6121"><tspan fill="rgb(0,0,0)" stroke="none">Northbound</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id12"> + <rect class="BoundingBox" stroke="none" fill="none" x="1738" y="7235" width="1735" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="1988" y="7618"><tspan fill="rgb(0,0,0)" stroke="none">pathd API</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.ClosedBezierShape"> + <g id="id13"> + <rect class="BoundingBox" stroke="none" fill="none" x="2299" y="2229" width="6957" height="8959"/> + <path fill="rgb(52,101,164)" stroke="none" d="M 2299,11134 L 2396,11134 2396,11160 2396,11186 2299,11186 2299,11134 Z M 2299,2230 L 2333,2230 2333,2256 2333,2283 2299,2283 2299,2230 Z M 2399,2283 L 2399,2256 2399,2230 2506,2230 2506,2256 2506,2283 2399,2283 Z M 2571,2283 L 2571,2256 2571,2230 2679,2230 2679,2256 2679,2283 2571,2283 Z M 2744,2283 L 2744,2256 2744,2230 2852,2230 2852,2256 2852,2283 2744,2283 Z M 2917,2283 L 2917,2256 2917,2230 3025,2230 3025,2256 3025,2283 2917,2283 Z M 3090,2283 L 3090,2256 3090,2230 3198,2230 3198,2256 3198,2283 3090,2283 Z M 3263,2283 L 3263,2256 3263,2230 3371,2230 3371,2256 3371,2283 3263,2283 Z M 3436,2283 L 3436,2256 3436,2230 3543,2230 3543,2256 3543,2283 3436,2283 Z M 3609,2283 L 3609,2256 3609,2230 3716,2230 3716,2256 3716,2283 3609,2283 Z M 3782,2283 L 3782,2256 3782,2230 3889,2230 3889,2256 3889,2283 3782,2283 Z M 3955,2283 L 3955,2256 3955,2230 4062,2230 4062,2256 4062,2283 3955,2283 Z M 4128,2283 L 4128,2256 4128,2230 4235,2230 4235,2256 4235,2283 4128,2283 Z M 4301,2283 L 4301,2256 4301,2230 4408,2230 4408,2256 4408,2283 4301,2283 Z M 4473,2283 L 4473,2256 4473,2230 4581,2230 4581,2256 4581,2283 4473,2283 Z M 4646,2283 L 4646,2256 4646,2230 4754,2230 4754,2256 4754,2283 4646,2283 Z M 4819,2283 L 4819,2256 4819,2230 4927,2230 4927,2256 4927,2283 4819,2283 Z M 4992,2283 L 4992,2256 4992,2230 5100,2230 5100,2256 5100,2283 4992,2283 Z M 5165,2283 L 5165,2256 5165,2230 5273,2230 5273,2256 5273,2283 5165,2283 Z M 5338,2283 L 5338,2256 5338,2230 5445,2230 5445,2256 5445,2283 5338,2283 Z M 5511,2283 L 5511,2256 5511,2230 5618,2230 5618,2256 5618,2283 5511,2283 Z M 5684,2283 L 5684,2256 5684,2230 5791,2230 5791,2256 5791,2283 5684,2283 Z M 5857,2283 L 5857,2256 5857,2230 5964,2230 5964,2256 5964,2283 5857,2283 Z M 6030,2283 L 6030,2256 6030,2230 6137,2230 6137,2256 6137,2283 6030,2283 Z M 6203,2283 L 6203,2256 6203,2230 6310,2230 6310,2256 6310,2283 6203,2283 Z M 6375,2283 L 6375,2256 6375,2230 6483,2230 6483,2256 6483,2283 6375,2283 Z M 6548,2283 L 6548,2256 6548,2230 6656,2230 6656,2256 6656,2283 6548,2283 Z M 6721,2283 L 6721,2256 6721,2230 6829,2230 6829,2256 6829,2283 6721,2283 Z M 6894,2283 L 6894,2256 6894,2230 7002,2230 7002,2256 7002,2283 6894,2283 Z M 7067,2283 L 7067,2256 7067,2230 7175,2230 7175,2256 7175,2283 7067,2283 Z M 7240,2283 L 7240,2256 7240,2230 7347,2230 7347,2256 7347,2283 7240,2283 Z M 7413,2283 L 7413,2256 7413,2230 7520,2230 7520,2256 7520,2283 7413,2283 Z M 7586,2283 L 7586,2256 7586,2230 7693,2230 7693,2256 7693,2283 7586,2283 Z M 7759,2283 L 7759,2256 7759,2230 7866,2230 7866,2256 7866,2283 7759,2283 Z M 7932,2283 L 7932,2256 7932,2230 8039,2230 8039,2256 8039,2283 7932,2283 Z M 8105,2283 L 8105,2256 8105,2230 8212,2230 8212,2256 8212,2283 8105,2283 Z M 8277,2283 L 8277,2256 8277,2230 8385,2230 8385,2256 8385,2283 8277,2283 Z M 8450,2283 L 8450,2256 8450,2230 8558,2230 8558,2256 8558,2283 8450,2283 Z M 8623,2283 L 8623,2256 8623,2230 8731,2230 8731,2256 8731,2283 8623,2283 Z M 8796,2283 L 8796,2256 8796,2230 8904,2230 8904,2256 8904,2283 8796,2283 Z M 8969,2283 L 8969,2256 8969,2230 9077,2230 9077,2256 9077,2283 8969,2283 Z M 9142,2283 L 9142,2256 9142,2230 9227,2230 C 9232,2230 9237,2231 9241,2233 9245,2235 9248,2239 9251,2243 9253,2247 9254,2251 9254,2256 L 9254,2278 9227,2278 9227,2283 9142,2283 Z M 9200,2341 L 9227,2341 9254,2341 9254,2446 9227,2446 9200,2446 9200,2341 Z M 9200,2509 L 9227,2509 9254,2509 9254,2614 9227,2614 9200,2614 9200,2509 Z M 9200,2677 L 9227,2677 9254,2677 9254,2782 9227,2782 9200,2782 9200,2677 Z M 9200,2845 L 9227,2845 9254,2845 9254,2950 9227,2950 9200,2950 9200,2845 Z M 9200,3013 L 9227,3013 9254,3013 9254,3118 9227,3118 9200,3118 9200,3013 Z M 9200,3181 L 9227,3181 9254,3181 9254,3286 9227,3286 9200,3286 9200,3181 Z M 9200,3349 L 9227,3349 9254,3349 9254,3454 9227,3454 9200,3454 9200,3349 Z M 9200,3517 L 9227,3517 9254,3517 9254,3622 9227,3622 9200,3622 9200,3517 Z M 9200,3685 L 9227,3685 9254,3685 9254,3790 9227,3790 9200,3790 9200,3685 Z M 9200,3853 L 9227,3853 9254,3853 9254,3958 9227,3958 9200,3958 9200,3853 Z M 9200,4021 L 9227,4021 9254,4021 9254,4126 9227,4126 9200,4126 9200,4021 Z M 9200,4189 L 9227,4189 9254,4189 9254,4294 9227,4294 9200,4294 9200,4189 Z M 9200,4357 L 9227,4357 9254,4357 9254,4462 9227,4462 9200,4462 9200,4357 Z M 9200,4525 L 9227,4525 9254,4525 9254,4630 9227,4630 9200,4630 9200,4525 Z M 9200,4693 L 9227,4693 9254,4693 9254,4798 9227,4798 9200,4798 9200,4693 Z M 9200,4861 L 9227,4861 9254,4861 9254,4966 9227,4966 9200,4966 9200,4861 Z M 9200,5029 L 9227,5029 9254,5029 9254,5134 9227,5134 9200,5134 9200,5029 Z M 9200,5197 L 9227,5197 9254,5197 9254,5302 9227,5302 9200,5302 9200,5197 Z M 9200,5365 L 9227,5365 9254,5365 9254,5470 9227,5470 9200,5470 9200,5365 Z M 9200,5533 L 9227,5533 9254,5533 9254,5638 9227,5638 9200,5638 9200,5533 Z M 9200,5701 L 9227,5701 9254,5701 9254,5806 9227,5806 9200,5806 9200,5701 Z M 9200,5869 L 9227,5869 9254,5869 9254,5974 9227,5974 9200,5974 9200,5869 Z M 9200,6037 L 9227,6037 9254,6037 9254,6142 9227,6142 9200,6142 9200,6037 Z M 9200,6205 L 9227,6205 9254,6205 9254,6310 9227,6310 9200,6310 9200,6205 Z M 9200,6373 L 9227,6373 9254,6373 9254,6478 9227,6478 9200,6478 9200,6373 Z M 9200,6541 L 9227,6541 9254,6541 9254,6646 9227,6646 9200,6646 9200,6541 Z M 9200,6709 L 9227,6709 9254,6709 9254,6814 9227,6814 9200,6814 9200,6709 Z M 9200,6877 L 9227,6877 9254,6877 9254,6982 9227,6982 9200,6982 9200,6877 Z M 9200,7045 L 9227,7045 9254,7045 9254,7150 9227,7150 9200,7150 9200,7045 Z M 9200,7213 L 9227,7213 9254,7213 9254,7318 9227,7318 9200,7318 9200,7213 Z M 9200,7381 L 9227,7381 9254,7381 9254,7486 9227,7486 9200,7486 9200,7381 Z M 9200,7549 L 9227,7549 9254,7549 9254,7654 9227,7654 9200,7654 9200,7549 Z M 9200,7717 L 9227,7717 9254,7717 9254,7822 9227,7822 9200,7822 9200,7717 Z M 9200,7886 L 9227,7886 9254,7886 9254,7990 9227,7990 9200,7990 9200,7886 Z M 9200,8054 L 9227,8054 9254,8054 9254,8158 9227,8158 9200,8158 9200,8054 Z M 9200,8222 L 9227,8222 9254,8222 9254,8326 9227,8326 9200,8326 9200,8222 Z M 9200,8390 L 9227,8390 9254,8390 9254,8494 9227,8494 9200,8494 9200,8390 Z M 9200,8558 L 9227,8558 9254,8558 9254,8662 9227,8662 9200,8662 9200,8558 Z M 9200,8726 L 9227,8726 9254,8726 9254,8830 9227,8830 9200,8830 9200,8726 Z M 9200,8894 L 9227,8894 9254,8894 9254,8998 9227,8998 9200,8998 9200,8894 Z M 9200,9062 L 9227,9062 9254,9062 9254,9166 9227,9166 9200,9166 9200,9062 Z M 9200,9230 L 9227,9230 9254,9230 9254,9334 9227,9334 9200,9334 9200,9230 Z M 9200,9398 L 9227,9398 9254,9398 9254,9502 9227,9502 9200,9502 9200,9398 Z M 9200,9566 L 9227,9566 9254,9566 9254,9670 9227,9670 9200,9670 9200,9566 Z M 9200,9734 L 9227,9734 9254,9734 9254,9838 9227,9838 9200,9838 9200,9734 Z M 9200,9902 L 9227,9902 9254,9902 9254,10006 9227,10006 9200,10006 9200,9902 Z M 9200,10070 L 9227,10070 9254,10070 9254,10174 9227,10174 9200,10174 9200,10070 Z M 9200,10238 L 9227,10238 9254,10238 9254,10342 9227,10342 9200,10342 9200,10238 Z M 9200,10406 L 9227,10406 9254,10406 9254,10510 9227,10510 9200,10510 9200,10406 Z M 9200,10574 L 9227,10574 9254,10574 9254,10678 9227,10678 9200,10678 9200,10574 Z M 9200,10742 L 9227,10742 9254,10742 9254,10846 9227,10846 9200,10846 9200,10742 Z M 9200,10910 L 9227,10910 9254,10910 9254,11014 9227,11014 9200,11014 9200,10910 Z M 9200,11078 L 9227,11078 9254,11078 9254,11160 C 9254,11165 9253,11169 9251,11173 9248,11177 9245,11181 9241,11183 9237,11185 9232,11186 9227,11186 L 9204,11186 9204,11160 9200,11160 9200,11078 Z M 9139,11134 L 9139,11160 9139,11186 9032,11186 9032,11160 9032,11134 9139,11134 Z M 8966,11134 L 8966,11160 8966,11186 8859,11186 8859,11160 8859,11134 8966,11134 Z M 8793,11134 L 8793,11160 8793,11186 8686,11186 8686,11160 8686,11134 8793,11134 Z M 8620,11134 L 8620,11160 8620,11186 8513,11186 8513,11160 8513,11134 8620,11134 Z M 8447,11134 L 8447,11160 8447,11186 8340,11186 8340,11160 8340,11134 8447,11134 Z M 8274,11134 L 8274,11160 8274,11186 8167,11186 8167,11160 8167,11134 8274,11134 Z M 8102,11134 L 8102,11160 8102,11186 7994,11186 7994,11160 7994,11134 8102,11134 Z M 7929,11134 L 7929,11160 7929,11186 7821,11186 7821,11160 7821,11134 7929,11134 Z M 7756,11134 L 7756,11160 7756,11186 7648,11186 7648,11160 7648,11134 7756,11134 Z M 7583,11134 L 7583,11160 7583,11186 7475,11186 7475,11160 7475,11134 7583,11134 Z M 7410,11134 L 7410,11160 7410,11186 7302,11186 7302,11160 7302,11134 7410,11134 Z M 7237,11134 L 7237,11160 7237,11186 7130,11186 7130,11160 7130,11134 7237,11134 Z M 7064,11134 L 7064,11160 7064,11186 6957,11186 6957,11160 6957,11134 7064,11134 Z M 6891,11134 L 6891,11160 6891,11186 6784,11186 6784,11160 6784,11134 6891,11134 Z M 6718,11134 L 6718,11160 6718,11186 6611,11186 6611,11160 6611,11134 6718,11134 Z M 6545,11134 L 6545,11160 6545,11186 6438,11186 6438,11160 6438,11134 6545,11134 Z M 6373,11134 L 6373,11160 6373,11186 6265,11186 6265,11160 6265,11134 6373,11134 Z M 6200,11134 L 6200,11160 6200,11186 6092,11186 6092,11160 6092,11134 6200,11134 Z M 6027,11134 L 6027,11160 6027,11186 5919,11186 5919,11160 5919,11134 6027,11134 Z M 5854,11134 L 5854,11160 5854,11186 5746,11186 5746,11160 5746,11134 5854,11134 Z M 5681,11134 L 5681,11160 5681,11186 5573,11186 5573,11160 5573,11134 5681,11134 Z M 5508,11134 L 5508,11160 5508,11186 5401,11186 5401,11160 5401,11134 5508,11134 Z M 5335,11134 L 5335,11160 5335,11186 5228,11186 5228,11160 5228,11134 5335,11134 Z M 5162,11134 L 5162,11160 5162,11186 5055,11186 5055,11160 5055,11134 5162,11134 Z M 4989,11134 L 4989,11160 4989,11186 4882,11186 4882,11160 4882,11134 4989,11134 Z M 4816,11134 L 4816,11160 4816,11186 4709,11186 4709,11160 4709,11134 4816,11134 Z M 4643,11134 L 4643,11160 4643,11186 4536,11186 4536,11160 4536,11134 4643,11134 Z M 4471,11134 L 4471,11160 4471,11186 4363,11186 4363,11160 4363,11134 4471,11134 Z M 4298,11134 L 4298,11160 4298,11186 4190,11186 4190,11160 4190,11134 4298,11134 Z M 4125,11134 L 4125,11160 4125,11186 4017,11186 4017,11160 4017,11134 4125,11134 Z M 3952,11134 L 3952,11160 3952,11186 3844,11186 3844,11160 3844,11134 3952,11134 Z M 3779,11134 L 3779,11160 3779,11186 3671,11186 3671,11160 3671,11134 3779,11134 Z M 3606,11134 L 3606,11160 3606,11186 3499,11186 3499,11160 3499,11134 3606,11134 Z M 3433,11134 L 3433,11160 3433,11186 3326,11186 3326,11160 3326,11134 3433,11134 Z M 3260,11134 L 3260,11160 3260,11186 3153,11186 3153,11160 3153,11134 3260,11134 Z M 3087,11134 L 3087,11160 3087,11186 2980,11186 2980,11160 2980,11134 3087,11134 Z M 2914,11134 L 2914,11160 2914,11186 2807,11186 2807,11160 2807,11134 2914,11134 Z M 2741,11134 L 2741,11160 2741,11186 2634,11186 2634,11160 2634,11134 2741,11134 Z M 2569,11134 L 2569,11160 2569,11186 2461,11186 2461,11160 2461,11134 2569,11134 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id14"> + <rect class="BoundingBox" stroke="none" fill="none" x="7086" y="3539" width="4194" height="1273"/> + <path fill="rgb(114,159,207)" stroke="none" d="M 9183,4810 L 7087,4810 7087,3540 11278,3540 11278,4810 9183,4810 Z"/> + <path fill="none" stroke="rgb(52,101,164)" d="M 9183,4810 L 7087,4810 7087,3540 11278,3540 11278,4810 9183,4810 Z"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="8218" y="4078"><tspan fill="rgb(0,0,0)" stroke="none">PCEPLIB</tspan></tspan></tspan><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="7900" y="4590"><tspan fill="rgb(0,0,0)" stroke="none">INTERFACE</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id15"> + <rect class="BoundingBox" stroke="none" fill="none" x="1665" y="6105" width="2033" height="205"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 1956,6207 L 3406,6207"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 1665,6207 L 1970,6309 1970,6106 1665,6207 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 3697,6207 L 3393,6106 3393,6309 3697,6207 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id16"> + <rect class="BoundingBox" stroke="none" fill="none" x="1665" y="5470" width="2033" height="205"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 1956,5572 L 3406,5572"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 1665,5572 L 1970,5674 1970,5471 1665,5572 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 3697,5572 L 3393,5471 3393,5674 3697,5572 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.CustomShape"> + <g id="id17"> + <rect class="BoundingBox" stroke="none" fill="none" x="4584" y="9553" width="9528" height="1146"/> + <path fill="rgb(114,159,207)" stroke="none" d="M 9348,10697 L 4585,10697 4585,9554 14110,9554 14110,10697 9348,10697 Z"/> + <path fill="none" stroke="rgb(52,101,164)" d="M 9348,10697 L 4585,10697 4585,9554 14110,9554 14110,10697 9348,10697 Z"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="459px" font-weight="400"><tspan class="TextPosition" x="7786" y="10285"><tspan fill="rgb(0,0,0)" stroke="none">CONTROLLER</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id18"> + <rect class="BoundingBox" stroke="none" fill="none" x="6491" y="9448" width="2383" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="6741" y="9831"><tspan fill="rgb(0,0,0)" stroke="none">pcep_ctrl_XXX</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id19"> + <rect class="BoundingBox" stroke="none" fill="none" x="9920" y="9448" width="2802" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="10170" y="9831"><tspan fill="rgb(0,0,0)" stroke="none">pcep_thread_XXX</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id20"> + <rect class="BoundingBox" stroke="none" fill="none" x="5953" y="8366" width="1682" height="1271"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 6185,8541 L 7402,9461"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 5953,8366 L 6135,8631 6257,8469 5953,8366 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 7634,9636 L 7452,9371 7330,9533 7634,9636 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id21"> + <rect class="BoundingBox" stroke="none" fill="none" x="5953" y="6207" width="2287" height="3430"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 6114,6449 L 8078,9394"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 5953,6207 L 6037,6517 6206,6404 5953,6207 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 8239,9636 L 8155,9326 7986,9439 8239,9636 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id22"> + <rect class="BoundingBox" stroke="none" fill="none" x="5953" y="4683" width="1652" height="763"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 6217,5323 L 7340,4805"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 5953,5445 L 6272,5410 6187,5225 5953,5445 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 7604,4683 L 7285,4718 7370,4903 7604,4683 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id23"> + <rect class="BoundingBox" stroke="none" fill="none" x="5953" y="4683" width="2287" height="3049"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 6128,7498 L 8064,4916"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 5953,7731 L 6217,7548 6054,7427 5953,7731 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 8239,4683 L 7975,4866 8138,4988 8239,4683 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id24"> + <rect class="BoundingBox" stroke="none" fill="none" x="8772" y="1635" width="205" height="2033"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 8874,3376 L 8874,1926"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 8874,3667 L 8976,3363 8773,3363 8874,3667 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 8874,1635 L 8773,1940 8976,1940 8874,1635 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.TextShape"> + <g id="id25"> + <rect class="BoundingBox" stroke="none" fill="none" x="8347" y="1127" width="1925" height="570"/> + <text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="282px" font-weight="400"><tspan class="TextPosition" x="8597" y="1510"><tspan fill="rgb(0,0,0)" stroke="none">pceplib API</tspan></tspan></tspan></text> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id26"> + <rect class="BoundingBox" stroke="none" fill="none" x="11033" y="4683" width="1779" height="1525"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 11254,4872 L 12590,6018"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 11033,4683 L 11198,4958 11330,4804 11033,4683 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 12811,6207 L 12646,5932 12514,6086 12811,6207 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id27"> + <rect class="BoundingBox" stroke="none" fill="none" x="11063" y="7223" width="1749" height="2414"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 11234,9400 L 12640,7459"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 11063,9636 L 11324,9449 11159,9330 11063,9636 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 12811,7223 L 12550,7410 12715,7529 12811,7223 Z"/> + </g> + </g> + <g class="com.sun.star.drawing.LineShape"> + <g id="id28"> + <rect class="BoundingBox" stroke="none" fill="none" x="9534" y="1635" width="205" height="2033"/> + <path fill="none" stroke="rgb(102,102,102)" stroke-width="35" stroke-linejoin="round" d="M 9636,3376 L 9636,1926"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 9636,3667 L 9738,3363 9535,3363 9636,3667 Z"/> + <path fill="rgb(102,102,102)" stroke="none" d="M 9636,1635 L 9535,1940 9738,1940 9636,1635 Z"/> + </g> + </g> + </g> + </g> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index 0c825d423..5e210e248 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -25,6 +25,33 @@ present and the :file:`frr.conf` is read instead. documented elsewhere. +PCEP Support +============ + +To build the PCC for pathd, the externall library `pceplib 1.2 <https://github.com/volta-networks/pceplib/tree/devel-1.2>`_ is required. + +To build FRR with support for PCEP the following steps must be followed: + + - Checkout and build pceplib: + +``` +$ git clone https://github.com/volta-networks/pceplib +$ cd pceplib +$ make +$ make install +$ export PCEPLIB_ROOT=$PWD +``` + + - Configure FRR with the extra parameters: + +``` +--enable-pcep LDFLAGS="-L${PCEPLIB_ROOT}/install/lib" CPPFLAGS="-I${PCEPLIB_ROOT}/install/include" +``` + +To start pathd with pcep support the extra parameter `-M pathd_pcep` should be +passed to the pathd daemon. + + Pathd Configuration =================== @@ -32,6 +59,7 @@ Example: .. code-block:: frr + debug pathd pcep basic segment-routing traffic-eng segment-list SL1 @@ -49,6 +77,25 @@ Example: metric te 10 objective-function mcp required ! + pcep + pce-config GROUP1 + source-address 1.1.1.1 + tcp-md5-auth secret + timer keep-alive 30 + ! + pce PCE1 + config GROUP1 + address ip 10.10.10.10 + ! + pce PCE2 + config GROUP1 + address ip 9.9.9.9 + ! + pcc + peer PCE1 precedence 10 + peer PCE2 precedence 20 + ! + ! ! ! @@ -177,6 +224,121 @@ Configuration Commands - msn: Minimize the number of Shared Nodes [RFC8800] +.. index:: [no] debug pathd pcep [basic|path|message|pceplib] +.. clicmd:: [no] debug pathd pcep [basic|path|message|pceplib] + + Enable or disable debugging for the pcep module: + + - basic: Enable basic PCEP logging + - path: Log the path structures + - message: Log the PCEP messages + - pceplib: Enable pceplib logging + + +.. index:: pcep +.. clicmd:: pcep + + Configure PCEP support. + + +.. index:: [no] cep-config NAME +.. clicmd:: [no] pce-config NAME + + Define a shared PCE configuration that can be used in multiple PCE + declarations. + + +.. index:: [no] pce NAME +.. clicmd:: [no] pce NAME + + Define or delete a PCE definition. + + +.. index:: config WORD +.. clicmd:: config WORD + + Select a shared configuration. If not defined, the default + configuration will be used. + + +.. index:: address <ip A.B.C.D | ipv6 X:X::X:X> [port (1024-65535)] +.. clicmd:: address <ip A.B.C.D | ipv6 X:X::X:X> [port (1024-65535)] + + Define the address and port of the PCE. + + If not specified, the port is the standard PCEP port 4189. + + This should be specified in the PCC peer definition. + + +.. index:: source-address [ip A.B.C.D | ipv6 X:X::X:X] [port PORT] +.. clicmd:: source-address [ip A.B.C.D | ipv6 X:X::X:X] [port PORT] + + Define the address and/or port of the PCC as seen by the PCE. + This can be used in a configuration group or a PCC peer declaration. + + If not specified, the source address will be the router identifier selected + by zebra, and the port will be the standard PCEP port 4189. + + This can be specified in either the PCC peer definition or in a + configuration group. + + +.. index:: tcp-md5-auth WORD +.. clicmd:: tcp-md5-auth WORD + + Enable TCP MD5 security with the given secret. + + This can be specified in either the PCC peer definition or in a + configuration group. + + +.. index:: sr-draft07 +.. clicmd:: sr-draft07 + + Specify if a PCE only support segment routing draft 7, this flag will limit + the PCC behavior to this draft. + + This can be specified in either the PCC peer definition or in a + configuration group. + + +.. index:: pce-initiated +.. clicmd:: pce-initiated + + Specify if PCE-initiated LSP should be allowed for this PCE. + + This can be specified in either the PCC peer definition or in a + configuration group. + + +.. index:: timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] [dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] [pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)] +.. clicmd:: timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] [dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] [pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)] + + Specify the PCEP timers. + + This can be specified in either the PCC peer definition or in a + configuration group. + + +.. index:: [no] pcc +.. clicmd:: [no] pcc + + Disable or start the definition of a PCC. + + +.. index:: msd (1-32) +.. clicmd:: msd (1-32) + + Specify the maximum SID depth in a PCC definition. + + +.. index:: [no] peer WORD [precedence (1-255)] +.. clicmd:: [no] peer WORD [precedence (1-255)] + + Specify a peer and its precedence in a PCC definition. + + Introspection Commands ---------------------- @@ -206,6 +368,54 @@ The asterisk (*) marks the best, e.g. active, candidate path. Note that for segm retrieved via PCEP a random number based name is generated. +.. index:: show debugging pathd +.. clicmd:: show debugging pathd + + Display the current status of the pathd debugging. + + +.. index:: show debugging pathd-pcep +.. clicmd:: show debugging pathd-pcep + + Display the current status of the pcep module debugging. + + +.. index:: show sr-te pcep counters +.. clicmd:: show sr-te pcep counters + + Display the counters from pceplib. + + +.. index:: show sr-te pcep pce-config [NAME] +.. clicmd:: show sr-te pcep pce-config [NAME] + + Display a shared configuration. if no name is specified, the default + configuration will be displayed. + + +.. index:: show sr-te pcep pcc +.. clicmd:: show sr-te pcep pcc + + Display PCC information. + + +.. index:: show sr-te pcep session [NAME] +.. clicmd:: show sr-te pcep session [NAME] + + Display the information of a PCEP session, if not name is specified all the + sessions will be displayed. + + +Utility Commands +---------------- + +.. index:: clear sr-te pcep session [NAME] +.. clicmd:: clear sr-te pcep session [NAME] + + Reset the pcep session by disconnecting from the PCE and performing the + normal reconnection process. No configuration is changed. + + Usage with BGP route-maps ========================= diff --git a/lib/command.c b/lib/command.c index eeb14b734..f40fe6e2c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -875,6 +875,18 @@ enum node_type node_parent(enum node_type node) case SR_CANDIDATE_DYN_NODE: ret = SR_POLICY_NODE; break; + case PCEP_NODE: + ret = SR_TRAFFIC_ENG_NODE; + break; + case PCEP_PCE_CONFIG_NODE: + ret = PCEP_NODE; + break; + case PCEP_PCE_NODE: + ret = PCEP_NODE; + break; + case PCEP_PCC_NODE: + ret = PCEP_NODE; + break; default: ret = CONFIG_NODE; break; diff --git a/lib/command.h b/lib/command.h index 2eae9a1b3..bfe64a723 100644 --- a/lib/command.h +++ b/lib/command.h @@ -150,6 +150,10 @@ enum node_type { SR_SEGMENT_LIST_NODE, /* SR segment list config node */ SR_POLICY_NODE, /* SR policy config node */ SR_CANDIDATE_DYN_NODE, /* SR dynamic candidate path config node */ + PCEP_NODE, /* PCEP node */ + PCEP_PCE_CONFIG_NODE, /* PCE shared configuration node */ + PCEP_PCE_NODE, /* PCE configuration node */ + PCEP_PCC_NODE, /* PCC configuration node */ VTY_NODE, /* Vty node. */ FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ diff --git a/lib/northbound.h b/lib/northbound.h index 3f6e4dc46..c37d66d58 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -600,6 +600,7 @@ enum nb_client { NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, NB_CLIENT_GRPC, + NB_CLIENT_PCEP, }; /* Northbound context. */ @@ -621,6 +622,8 @@ struct nb_context { } sysrepo; struct { } grpc; + struct { + } pcep; } client_data; #endif }; diff --git a/pathd/path_errors.c b/pathd/path_errors.c index f266b6b4e..f8560a848 100644 --- a/pathd/path_errors.c +++ b/pathd/path_errors.c @@ -25,14 +25,105 @@ /* clang-format off */ static struct log_ref ferr_path_err[] = { { + .code = EC_PATH_SYSTEM_CALL, + .title = "Thread setup error", + .description = "A system call for creating, or setting up PCEP module's pthread failed", + .suggestion = "Open an Issue with all relevant log files and restart FRR" + }, + { + .code = EC_PATH_PCEP_PCC_INIT, + .title = "PCC initialization error", + .description = "pceplib PCC initialization call failed", + .suggestion = "Open an Issue with all relevant log files and restart FRR" + }, + { + .code = EC_PATH_PCEP_PCC_FINI, + .title = "PCC finalization error", + .description = "pceplib PCC finalization call failed", + .suggestion = "Open an Issue with all relevant log files and restart FRR" + }, + { + .code = EC_PATH_PCEP_PCC_CONF_UPDATE, + .title = "PCC configuration update error", + .description = "The update of the PCC configuration failed", + .suggestion = "Open an Issue with all relevant log files and restart FRR" + }, + { .code = END_FERR, } }; static struct log_ref ferr_path_warn[] = { { + .code = EC_PATH_PCEP_LIB_CONNECT, + .title = "PCC connection error", + .description = "The PCEP module failed to connected to configured PCE", + .suggestion = "Check the connectivity between the PCC and the PCE" + }, + { + .code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + .title = "PCC connection error", + .description = "The PCEP module did not try to connect because it is missing a source address", + .suggestion = "Wait for the router ID to be defined or set the PCC source address in the configuration" + }, + { + .code = EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + .title = "Recoverable internal error", + .description = "Some recoverable internal error", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + .title = "Unsupported PCEP feature", + .description = "Receved an unsupported PCEP message", + .suggestion = "The PCC and PCE are probably not compatible. Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE, + .title = "Unexpected PCEP message", + .description = "The PCEP module received an unexpected PCEP message", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT, + .title = "Unexpected pceplib event", + .description = "The PCEP module received an unexpected event from pceplib", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, + .title = "Unexpected PCEP object", + .description = "The PCEP module received an unexpected PCEP object from a PCE", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + .title = "Unexpected PCEP TLV", + .description = "The PCEP module received an unexpected PCEP TLV from a PCE", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ, + .title = "Unexpected PCEP ERO sub-object", + .description = "The PCEP module received an unexpected PCEP ERO sub-object from a PCE", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_UNEXPECTED_SR_NAI, + .title = "Unexpected PCEP SR segment NAI", + .description = "The PCEP module received an SR segment with an unsupported NAI specification from the PCE", + .suggestion = "Open an Issue with all relevant log files" + }, + { + .code = EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT, + .title = "Computation request timeout", + .description = "The PCE did not respond in time to the PCC computation request", + .suggestion = "The PCE is overloaded or incompatible with the PCC, try with a different PCE" + }, + { .code = END_FERR, } + }; /* clang-format on */ diff --git a/pathd/path_errors.h b/pathd/path_errors.h index 366878f30..72e127f26 100644 --- a/pathd/path_errors.h +++ b/pathd/path_errors.h @@ -23,6 +23,21 @@ enum path_log_refs { EC_PATH_PCEP_INIT = PATH_FERR_START, + EC_PATH_SYSTEM_CALL, + EC_PATH_PCEP_PCC_INIT, + EC_PATH_PCEP_PCC_FINI, + EC_PATH_PCEP_PCC_CONF_UPDATE, + EC_PATH_PCEP_LIB_CONNECT, + EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE, + EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT, + EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, + EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ, + EC_PATH_PCEP_UNEXPECTED_SR_NAI, + EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT }; extern void path_error_init(void); diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c new file mode 100644 index 000000000..2f9ff4f0f --- /dev/null +++ b/pathd/path_pcep.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <pcep_utils_counters.h> + +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" +#include "termtable.h" + +#include "pathd/pathd.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep_memory.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_cli.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_config.h" + + +/* + * Globals. + */ +static struct pcep_glob pcep_glob_space = {.dbg = {0, "pathd module: pcep"}}; +struct pcep_glob *pcep_g = &pcep_glob_space; + +/* Main Thread Even Handler */ +static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, + void *payload); +static int pcep_main_event_start_sync(int pcc_id); +static int pcep_main_event_start_sync_cb(struct path *path, void *arg); +static int pcep_main_event_update_candidate(struct path *path); +static int pcep_main_event_remove_candidate_segments(const char *originator, + bool force); + +/* Hook Handlers called from the Main Thread */ +static int pathd_candidate_created_handler(struct srte_candidate *candidate); +static int pathd_candidate_updated_handler(struct srte_candidate *candidate); +static int pathd_candidate_removed_handler(struct srte_candidate *candidate); + +/* Path manipulation functions */ +static struct path_metric *pcep_copy_metrics(struct path_metric *metric); +static struct path_hop *pcep_copy_hops(struct path_hop *hop); + +/* Module Functions */ +static int pcep_module_finish(void); +static int pcep_module_late_init(struct thread_master *tm); +static int pcep_module_init(void); + +/* ------------ Path Helper Functions ------------ */ + +struct path *pcep_new_path(void) +{ + struct path *path; + path = XCALLOC(MTYPE_PCEP, sizeof(*path)); + path->binding_sid = MPLS_LABEL_NONE; + path->enforce_bandwidth = true; + return path; +} + +struct path_hop *pcep_new_hop(void) +{ + struct path_hop *hop; + hop = XCALLOC(MTYPE_PCEP, sizeof(*hop)); + return hop; +} + +struct path_metric *pcep_new_metric(void) +{ + struct path_metric *metric; + metric = XCALLOC(MTYPE_PCEP, sizeof(*metric)); + return metric; +} + +struct path_metric *pcep_copy_metrics(struct path_metric *metric) +{ + if (metric == NULL) + return NULL; + struct path_metric *new_metric = pcep_new_metric(); + *new_metric = *metric; + new_metric->next = pcep_copy_metrics(metric->next); + return new_metric; +} + +struct path_hop *pcep_copy_hops(struct path_hop *hop) +{ + if (hop == NULL) + return NULL; + struct path_hop *new_hop = pcep_new_hop(); + *new_hop = *hop; + new_hop->next = pcep_copy_hops(hop->next); + return new_hop; +} + +struct path *pcep_copy_path(struct path *path) +{ + struct path *new_path = pcep_new_path(); + + *new_path = *path; + new_path->first_metric = pcep_copy_metrics(path->first_metric); + new_path->first_hop = pcep_copy_hops(path->first_hop); + if (path->name != NULL) + new_path->name = XSTRDUP(MTYPE_PCEP, path->name); + if (path->originator != NULL) + new_path->originator = XSTRDUP(MTYPE_PCEP, path->originator); + return new_path; +} + +void pcep_free_path(struct path *path) +{ + struct path_hop *hop; + struct path_metric *metric; + char *tmp; + + metric = path->first_metric; + while (metric != NULL) { + struct path_metric *next = metric->next; + XFREE(MTYPE_PCEP, metric); + metric = next; + } + hop = path->first_hop; + while (hop != NULL) { + struct path_hop *next = hop->next; + XFREE(MTYPE_PCEP, hop); + hop = next; + } + if (path->originator != NULL) { + /* The path own the memory, it is const so it is clear it + shouldn't be modified. XFREE macro do not support type casting + so we need a temporary variable */ + tmp = (char *)path->originator; + XFREE(MTYPE_PCEP, tmp); + path->originator = NULL; + } + if (path->name != NULL) { + /* The path own the memory, it is const so it is clear it + shouldn't be modified. XFREE macro do not support type casting + so we need a temporary variable */ + tmp = (char *)path->name; + XFREE(MTYPE_PCEP, tmp); + path->name = NULL; + } + XFREE(MTYPE_PCEP, path); +} + + +/* ------------ Main Thread Even Handler ------------ */ + +int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id, + void *payload) +{ + int ret = 0; + + switch (type) { + case PCEP_MAIN_EVENT_START_SYNC: + ret = pcep_main_event_start_sync(pcc_id); + break; + case PCEP_MAIN_EVENT_UPDATE_CANDIDATE: + assert(payload != NULL); + ret = pcep_main_event_update_candidate((struct path *)payload); + break; + case PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP: + ret = pcep_main_event_remove_candidate_segments( + (const char *)payload, true); + break; + default: + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Unexpected event received in the main thread: %u", + type); + break; + } + + return ret; +} + +int pcep_main_event_start_sync(int pcc_id) +{ + path_pcep_config_list_path(pcep_main_event_start_sync_cb, &pcc_id); + pcep_ctrl_sync_done(pcep_g->fpt, pcc_id); + return 0; +} + +int pcep_main_event_start_sync_cb(struct path *path, void *arg) +{ + int *pcc_id = (int *)arg; + pcep_ctrl_sync_path(pcep_g->fpt, *pcc_id, path); + return 1; +} + +int pcep_main_event_update_candidate(struct path *path) +{ + struct path *resp = NULL; + int ret = 0; + + ret = path_pcep_config_update_path(path); + if (ret != PATH_NB_ERR && path->srp_id != 0) { + /* ODL and Cisco requires the first reported + * LSP to have a DOWN status, the later status changes + * will be comunicated through hook calls. + */ + enum pcep_lsp_operational_status real_status; + if ((resp = path_pcep_config_get_path(&path->nbkey))) { + resp->srp_id = path->srp_id; + real_status = resp->status; + resp->status = PCEP_LSP_OPERATIONAL_DOWN; + pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp); + /* If the update did not have any effect and the real + * status is not DOWN, we need to send a second report + * so the PCE is aware of the real status. This is due + * to the fact that NO notification will be received + * if the update did not apply any changes */ + if ((ret == PATH_NB_NO_CHANGE) + && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) { + resp->status = real_status; + resp->srp_id = 0; + pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, + resp); + } + pcep_free_path(resp); + } + } + return ret; +} + +int pcep_main_event_remove_candidate_segments(const char *originator, + bool force) +{ + srte_candidate_unset_segment_list(originator, force); + /* Avoid compiler warnings about const char* */ + void *free_ptr = (void *)originator; + XFREE(MTYPE_PCEP, free_ptr); + + srte_apply_changes(); + + return 0; +} + +/* ------------ Hook Handlers Functions Called From Main Thread ------------ */ + +int pathd_candidate_created_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_CREATED, path); + return ret; +} + +int pathd_candidate_updated_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_UPDATED, path); + return ret; +} + +int pathd_candidate_removed_handler(struct srte_candidate *candidate) +{ + struct path *path = candidate_to_path(candidate); + int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_REMOVED, path); + return ret; +} + + +/* ------------ Module Functions ------------ */ + +int pcep_module_late_init(struct thread_master *tm) +{ + assert(pcep_g->fpt == NULL); + assert(pcep_g->master == NULL); + + struct frr_pthread *fpt; + + if (pcep_ctrl_initialize(tm, &fpt, pcep_main_event_handler)) + return 1; + + if (pcep_lib_initialize(fpt)) + return 1; + + pcep_g->master = tm; + pcep_g->fpt = fpt; + + hook_register(pathd_candidate_created, pathd_candidate_created_handler); + hook_register(pathd_candidate_updated, pathd_candidate_updated_handler); + hook_register(pathd_candidate_removed, pathd_candidate_removed_handler); + + hook_register(frr_fini, pcep_module_finish); + + pcep_cli_init(); + + return 0; +} + +int pcep_module_finish(void) +{ + pcep_ctrl_finalize(&pcep_g->fpt); + pcep_lib_finalize(); + + for (int i = 0; i < MAX_PCC; i++) + if (pcep_g->pce_opts_cli[i] != NULL) + XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]); + + return 0; +} + +int pcep_module_init(void) +{ + pcep_g->num_pce_opts_cli = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->pce_opts_cli[i] = NULL; + pcep_g->num_config_group_opts = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->config_group_opts[i] = NULL; + + hook_register(frr_late_init, pcep_module_late_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "frr_pathd_pcep", .version = FRR_VERSION, + .description = "FRR pathd PCEP module", + .init = pcep_module_init) diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h new file mode 100644 index 000000000..1896c265c --- /dev/null +++ b/pathd/path_pcep.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_H_ +#define _PATH_PCEP_H_ + +#include <stdbool.h> +#include <debug.h> +#include <netinet/tcp.h> +#include <pcep_utils_logging.h> +#include <pcep_pcc_api.h> +#include "mpls.h" +#include "pathd/pathd.h" +#include "pathd/path_pcep_memory.h" + +#define PCEP_DEFAULT_PORT 4189 +#define MAX_PCC 32 +#define MAX_PCE 32 +#define MAX_TAG_SIZE 50 +#define PCEP_DEBUG_MODE_BASIC 0x01 +#define PCEP_DEBUG_MODE_PATH 0x02 +#define PCEP_DEBUG_MODE_PCEP 0x04 +#define PCEP_DEBUG_MODE_PCEPLIB 0x08 +#define PCEP_DEBUG(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) \ + DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \ + } while (0) +#define PCEP_DEBUG_PATH(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) \ + DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \ + } while (0) +#define PCEP_DEBUG_PCEP(fmt, ...) \ + do { \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) \ + DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \ + } while (0) +#define PCEP_DEBUG_PCEPLIB(priority, fmt, ...) \ + do { \ + switch (priority) { \ + case LOG_DEBUG: \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \ + PCEP_DEBUG_MODE_PCEPLIB)) \ + DEBUGD(&pcep_g->dbg, "pcep: " fmt, \ + ##__VA_ARGS__); \ + break; \ + case LOG_INFO: \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \ + PCEP_DEBUG_MODE_PCEPLIB)) \ + DEBUGI(&pcep_g->dbg, "pcep: " fmt, \ + ##__VA_ARGS__); \ + break; \ + case LOG_NOTICE: \ + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \ + PCEP_DEBUG_MODE_PCEPLIB)) \ + DEBUGN(&pcep_g->dbg, "pcep: " fmt, \ + ##__VA_ARGS__); \ + break; \ + case LOG_WARNING: \ + case LOG_ERR: \ + default: \ + zlog(priority, "pcep: " fmt, ##__VA_ARGS__); \ + break; \ + } \ + } while (0) + +struct pcep_config_group_opts { + char name[64]; + char tcp_md5_auth[TCP_MD5SIG_MAXKEYLEN]; + struct ipaddr source_ip; + short source_port; + bool draft07; + bool pce_initiated; + int keep_alive_seconds; + int min_keep_alive_seconds; + int max_keep_alive_seconds; + int dead_timer_seconds; + int min_dead_timer_seconds; + int max_dead_timer_seconds; + int pcep_request_time_seconds; + int session_timeout_inteval_seconds; + int delegation_timeout_seconds; +}; + +struct pce_opts { + struct ipaddr addr; + short port; + char pce_name[64]; + struct pcep_config_group_opts config_opts; + uint8_t precedence; /* Multi-PCE precedence */ +}; + +struct pcc_opts { + struct ipaddr addr; + short port; + short msd; +}; + +/* Encapsulate the pce_opts with needed CLI information */ +struct pce_opts_cli { + struct pce_opts pce_opts; + char config_group_name[64]; + /* These are the values configured in the pcc-peer sub-commands. + * These need to be stored for later merging. Notice, it could + * be that not all of them are set. */ + struct pcep_config_group_opts pce_config_group_opts; + /* The pce_opts->config_opts will be a merge of the default values, + * optional config_group values (which overwrite default values), + * and any values configured in the pce sub-commands (which overwrite + * both default and config_group values). This flag indicates of the + * values need to be merged or not. */ + bool merged; +}; + +struct lsp_nb_key { + uint32_t color; + struct ipaddr endpoint; + uint32_t preference; +}; + +struct sid_mpls { + mpls_label_t label; + uint8_t traffic_class; + bool is_bottom; + uint8_t ttl; +}; + +struct pcep_caps { + bool is_stateful; + /* If we know the objective functions supported by the PCE. + * If we don't know, it doesn't mean the PCE doesn't support any */ + bool supported_ofs_are_known; + /* Defined if we know which objective funtions are supported by the PCE. + * One bit per objective function, the bit index being equal to + * enum pcep_objfun_type values: bit 0 is not used, bit 1 is + * PCEP_OBJFUN_MCP, up to bit 17 that is PCEP_OBJFUN_MSN */ + uint32_t supported_ofs; +}; + +union sid { + uint32_t value; + struct sid_mpls mpls; +}; + +struct nai { + /* NAI type */ + enum pcep_sr_subobj_nai type; + /* Local IP address*/ + struct ipaddr local_addr; + /* Local interface identifier if the NAI is an unnumbered adjacency */ + uint32_t local_iface; + /* Remote address if the NAI is an adjacency */ + struct ipaddr remote_addr; + /* Remote interface identifier if the NAI is an unnumbered adjacency */ + uint32_t remote_iface; +}; + +struct path_hop { + /* Pointer to the next hop in the path */ + struct path_hop *next; + /* Indicateif this ia a loose or strict hop */ + bool is_loose; + /* Indicate if there is an SID for the hop */ + bool has_sid; + /* Indicate if the hop as a MPLS label */ + bool is_mpls; + /* Indicate if the MPLS label has extra attributes (TTL, class..)*/ + bool has_attribs; + /* Hop's SID if available */ + union sid sid; + /* Indicate if there is a NAI for this hop */ + bool has_nai; + /* NAI if available */ + struct nai nai; +}; + +struct path_metric { + /* Pointer to the next metric */ + struct path_metric *next; + /* The metric type */ + enum pcep_metric_types type; + /* If the metric should be enforced */ + bool enforce; + /* If the metric value is bound (a maximum) */ + bool is_bound; + /* If the metric value is computed */ + bool is_computed; + /* The metric value */ + float value; +}; + +struct path { + /* Both the nbkey and the plspid are keys comming from the PCC, + but the PCE is only using the plspid. The missing key is looked up by + the PCC so we always have both */ + + /* The northbound key identifying this path */ + struct lsp_nb_key nbkey; + /* The generated unique PLSP identifier for this path. + See draft-ietf-pce-stateful-pce */ + uint32_t plsp_id; + + /* The transport address the path is comming from, PCE or PCC*/ + struct ipaddr sender; + /* The pcc protocol address, must be the same family as the endpoint */ + struct ipaddr pcc_addr; + + /* The identifier of the PCC the path is for/from. If 0 it is undefined, + meaning it hasn't be set yet or is for all the PCC */ + int pcc_id; + + /* The origin of the path creation */ + enum srte_protocol_origin create_origin; + /* The origin of the path modification */ + enum srte_protocol_origin update_origin; + /* The identifier of the entity that originated the path */ + const char *originator; + /* The type of the path, for PCE initiated or updated path it is always + SRTE_CANDIDATE_TYPE_DYNAMIC */ + enum srte_candidate_type type; + + /* The following data comes from either the PCC or the PCE if available + */ + + /* Path's binding SID */ + mpls_label_t binding_sid; + /* The name of the path */ + const char *name; + /* The request identifier from the PCE, when getting a path from the + PCE. See draft-ietf-pce-stateful-pce */ + uint32_t srp_id; + /* The request identifier from the PCC , when getting a path from the + PCE after a computation request. See rfc5440, section-7.4 */ + uint32_t req_id; + /* The operational status of the path */ + enum pcep_lsp_operational_status status; + /* If true, the receiver (PCC) must remove the path. + See draft-ietf-pce-pce-initiated-lsp */ + bool do_remove; + /* Indicate the given path was removed by the PCC. + See draft-ietf-pce-stateful-pce, section-7.3, flag R */ + bool was_removed; + /* Indicate the path is part of the synchronization process. + See draft-ietf-pce-stateful-pce, section-7.3, flag S */ + bool is_synching; + /* Indicate if the path bandwidth requirment is defined */ + bool has_bandwidth; + /* Indicate if the bandwidth requirment should be enforced */ + bool enforce_bandwidth; + /* Path required bandwidth if defined */ + float bandwidth; + /* Specify the list of hop defining the path */ + struct path_hop *first_hop; + /* Specify the list of metrics */ + struct path_metric *first_metric; + /* Indicate if the path has a PCC-defined objective function */ + bool has_pcc_objfun; + /* Indicate the PCC-defined objective function is required */ + bool enforce_pcc_objfun; + /* PCC-defined Objective Function */ + enum objfun_type pcc_objfun; + /* Indicate if the path has a PCE-defined objective function */ + bool has_pce_objfun; + /* PCE-defined Objective Function */ + enum objfun_type pce_objfun; + /* Indicate if some affinity filters are defined */ + bool has_affinity_filters; + /* Affinity attribute filters indexed by enum affinity_filter_type - 1 + */ + uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE]; + + /* The following data need to be specialized for a given PCE */ + + /* Indicate the path is delegated to the PCE. + See draft-ietf-pce-stateful-pce, section-7.3, flag D */ + bool is_delegated; + /* Indicate if the PCE wants the path to get active. + See draft-ietf-pce-stateful-pce, section-7.3, flag A */ + bool go_active; + /* Indicate the given path was created by the PCE, + See draft-ietf-pce-pce-initiated-lsp, section-5.3.1, flag C */ + bool was_created; + + /* The following data is defined for comnputation replies */ + + /* Indicate that no path could be computed */ + bool no_path; +}; + +struct pcep_glob { + struct debug dbg; + struct thread_master *master; + struct frr_pthread *fpt; + uint8_t num_pce_opts_cli; + struct pce_opts_cli *pce_opts_cli[MAX_PCE]; + uint8_t num_config_group_opts; + struct pcep_config_group_opts *config_group_opts[MAX_PCE]; +}; + +extern struct pcep_glob *pcep_g; + +/* Path Helper Functions */ +struct path *pcep_new_path(void); +struct path_hop *pcep_new_hop(void); +struct path_metric *pcep_new_metric(void); +struct path *pcep_copy_path(struct path *path); +void pcep_free_path(struct path *path); + + +#endif // _PATH_PCEP_H_ diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c new file mode 100644 index 000000000..f80814f17 --- /dev/null +++ b/pathd/path_pcep_cli.c @@ -0,0 +1,2029 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * Brady Johnson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <pcep_utils_counters.h> +#include <pcep_session_logic.h> + +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" +#include "termtable.h" + +#include "pathd/pathd.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep_memory.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_cli.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_debug.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_pcc.h" + +#ifndef VTYSH_EXTRACT_PL +#include "pathd/path_pcep_cli_clippy.c" +#endif + +#define DEFAULT_PCE_PRECEDENCE 255 +#define DEFAULT_PCC_MSD 4 +#define DEFAULT_SR_DRAFT07 false +#define DEFAULT_PCE_INITIATED false +#define DEFAULT_TIMER_KEEP_ALIVE 30 +#define DEFAULT_TIMER_KEEP_ALIVE_MIN 1 +#define DEFAULT_TIMER_KEEP_ALIVE_MAX 255 +#define DEFAULT_TIMER_DEADTIMER 120 +#define DEFAULT_TIMER_DEADTIMER_MIN 4 +#define DEFAULT_TIMER_DEADTIMER_MAX 255 +#define DEFAULT_TIMER_PCEP_REQUEST 30 +#define DEFAULT_TIMER_SESSION_TIMEOUT_INTERVAL 30 +#define DEFAULT_DELEGATION_TIMEOUT_INTERVAL 10 + +/* CLI Function declarations */ +static int pcep_cli_debug_config_write(struct vty *vty); +static int pcep_cli_debug_set_all(uint32_t flags, bool set); +static int pcep_cli_pcep_config_write(struct vty *vty); +static int pcep_cli_pcc_config_write(struct vty *vty); +static int pcep_cli_pce_config_write(struct vty *vty); +static int pcep_cli_pcep_pce_config_write(struct vty *vty); + +/* Internal Util Function declarations */ +static struct pce_opts_cli *pcep_cli_find_pce(const char *pce_name); +static bool pcep_cli_add_pce(struct pce_opts_cli *pce_opts_cli); +static struct pce_opts_cli *pcep_cli_create_pce_opts(); +static void pcep_cli_delete_pce(const char *pce_name); +static void +pcep_cli_merge_pcep_pce_config_options(struct pce_opts_cli *pce_opts_cli); +static struct pcep_config_group_opts * +pcep_cli_find_pcep_pce_config(const char *group_name); +static bool +pcep_cli_add_pcep_pce_config(struct pcep_config_group_opts *config_group_opts); +static struct pcep_config_group_opts * +pcep_cli_create_pcep_pce_config(const char *group_name); +static bool pcep_cli_is_pcep_pce_config_used(const char *group_name); +static void pcep_cli_delete_pcep_pce_config(const char *group_name); +static int pcep_cli_print_pce_config(struct pcep_config_group_opts *group_opts, + char *buf, size_t buf_len); +static void print_pcep_capabilities(char *buf, size_t buf_len, + pcep_configuration *config); +static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, + struct pcep_pcc_info *pcc_info); +static bool pcep_cli_pcc_has_pce(const char *pce_name); +static void pcep_cli_add_pce_connection(struct pce_opts *pce_opts); +static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts); +static int path_pcep_cli_pcc_pcc_peer_delete(struct vty *vty, + const char *peer_name, + const char *precedence_str, + long precedence); + +/* + * Globals. + */ + +static const char PCEP_VTYSH_ARG_ADDRESS[] = "address"; +static const char PCEP_VTYSH_ARG_SOURCE_ADDRESS[] = "source-address"; +static const char PCEP_VTYSH_ARG_IP[] = "ip"; +static const char PCEP_VTYSH_ARG_IPV6[] = "ipv6"; +static const char PCEP_VTYSH_ARG_PORT[] = "port"; +static const char PCEP_VTYSH_ARG_PRECEDENCE[] = "precedence"; +static const char PCEP_VTYSH_ARG_MSD[] = "msd"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE[] = "keep-alive"; +static const char PCEP_VTYSH_ARG_TIMER[] = "timer"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE_MIN[] = "min-peer-keep-alive"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE_MAX[] = "max-peer-keep-alive"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER[] = "dead-timer"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER_MIN[] = "min-peer-dead-timer"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER_MAX[] = "max-peer-dead-timer"; +static const char PCEP_VTYSH_ARG_PCEP_REQUEST[] = "pcep-request"; +static const char PCEP_VTYSH_ARG_SESSION_TIMEOUT[] = "session-timeout-interval"; +static const char PCEP_VTYSH_ARG_DELEGATION_TIMEOUT[] = "delegation-timeout"; +static const char PCEP_VTYSH_ARG_SR_DRAFT07[] = "sr-draft07"; +static const char PCEP_VTYSH_ARG_PCE_INIT[] = "pce-initiated"; +static const char PCEP_VTYSH_ARG_TCP_MD5[] = "tcp-md5-auth"; +static const char PCEP_VTYSH_ARG_BASIC[] = "basic"; +static const char PCEP_VTYSH_ARG_PATH[] = "path"; +static const char PCEP_VTYSH_ARG_MESSAGE[] = "message"; +static const char PCEP_VTYSH_ARG_PCEPLIB[] = "pceplib"; +static const char PCEP_CLI_CAP_STATEFUL[] = " [Stateful PCE]"; +static const char PCEP_CLI_CAP_INCL_DB_VER[] = " [Include DB version]"; +static const char PCEP_CLI_CAP_LSP_TRIGGERED[] = " [LSP Triggered Resync]"; +static const char PCEP_CLI_CAP_LSP_DELTA[] = " [LSP Delta Sync]"; +static const char PCEP_CLI_CAP_PCE_TRIGGERED[] = + " [PCE triggered Initial Sync]"; +static const char PCEP_CLI_CAP_SR_TE_PST[] = " [SR TE PST]"; +static const char PCEP_CLI_CAP_PCC_RESOLVE_NAI[] = + " [PCC can resolve NAI to SID]"; +static const char PCEP_CLI_CAP_PCC_INITIATED[] = " [PCC Initiated LSPs]"; +static const char PCEP_CLI_CAP_PCC_PCE_INITIATED[] = + " [PCC and PCE Initiated LSPs]"; + +struct pce_connections { + int num_connections; + struct pce_opts *connections[MAX_PCC]; +}; + +struct pce_connections pce_connections_g = {.num_connections = 0}; + +/* Default PCE group that all PCE-Groups and PCEs will inherit from */ +struct pcep_config_group_opts default_pcep_config_group_opts_g = { + .name = "default", + .tcp_md5_auth = "\0", + .draft07 = DEFAULT_SR_DRAFT07, + .pce_initiated = DEFAULT_PCE_INITIATED, + .keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE, + .min_keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE_MIN, + .max_keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE_MAX, + .dead_timer_seconds = DEFAULT_TIMER_DEADTIMER, + .min_dead_timer_seconds = DEFAULT_TIMER_DEADTIMER_MIN, + .max_dead_timer_seconds = DEFAULT_TIMER_DEADTIMER_MAX, + .pcep_request_time_seconds = DEFAULT_TIMER_PCEP_REQUEST, + .session_timeout_inteval_seconds = + DEFAULT_TIMER_SESSION_TIMEOUT_INTERVAL, + .delegation_timeout_seconds = DEFAULT_DELEGATION_TIMEOUT_INTERVAL, + .source_port = DEFAULT_PCEP_TCP_PORT, + .source_ip.ipa_type = IPADDR_NONE, +}; + +/* Used by PCEP_PCE_CONFIG_NODE sub-commands to operate on the current pce group + */ +struct pcep_config_group_opts *current_pcep_config_group_opts_g = NULL; +/* Used by PCEP_PCE_NODE sub-commands to operate on the current pce opts */ +struct pce_opts_cli *current_pce_opts_g = NULL; +short pcc_msd_g = DEFAULT_PCC_MSD; +bool pcc_msd_configured_g = false; + +static struct cmd_node pcep_node = { + .name = "srte pcep", + .node = PCEP_NODE, + .parent_node = SR_TRAFFIC_ENG_NODE, + .config_write = pcep_cli_pcep_config_write, + .prompt = "%s(config-sr-te-pcep)# " +}; + +static struct cmd_node pcep_pcc_node = { + .name = "srte pcep pcc", + .node = PCEP_PCC_NODE, + .parent_node = PCEP_NODE, + .config_write = pcep_cli_pcc_config_write, + .prompt = "%s(config-sr-te-pcep-pcc)# " +}; + +static struct cmd_node pcep_pce_node = { + .name = "srte pcep pce", + .node = PCEP_PCE_NODE, + .parent_node = PCEP_NODE, + .config_write = pcep_cli_pce_config_write, + .prompt = "%s(config-sr-te-pcep-pce)# " +}; + +static struct cmd_node pcep_pce_config_node = { + .name = "srte pcep pce-config", + .node = PCEP_PCE_CONFIG_NODE, + .parent_node = PCEP_NODE, + .config_write = pcep_cli_pcep_pce_config_write, + .prompt = "%s(pce-sr-te-pcep-pce-config)# " +}; + +/* Common code used in VTYSH processing for int values */ +#define PCEP_VTYSH_INT_ARG_CHECK(arg_str, arg_val, arg_store, min_value, \ + max_value) \ + if (arg_str != NULL) { \ + if (arg_val <= min_value || arg_val >= max_value) { \ + vty_out(vty, \ + "%% Invalid value %ld in range [%d - %d]", \ + arg_val, min_value, max_value); \ + return CMD_WARNING; \ + } \ + arg_store = arg_val; \ + } + +#define MERGE_COMPARE_CONFIG_GROUP_VALUE(config_param, not_set_value) \ + pce_opts_cli->pce_opts.config_opts.config_param = \ + pce_opts_cli->pce_config_group_opts.config_param; \ + if (pce_opts_cli->pce_config_group_opts.config_param \ + == not_set_value) { \ + pce_opts_cli->pce_opts.config_opts.config_param = \ + ((pce_config != NULL \ + && pce_config->config_param != not_set_value) \ + ? pce_config->config_param \ + : default_pcep_config_group_opts_g \ + .config_param); \ + } + +/* + * Internal Util functions + */ + +/* Check if a pce_opts_cli already exists based on its name and return it, + * return NULL otherwise */ +static struct pce_opts_cli *pcep_cli_find_pce(const char *pce_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + struct pce_opts_cli *pce_rhs_cli = pcep_g->pce_opts_cli[i]; + if (pce_rhs_cli != NULL) { + if (strcmp(pce_name, pce_rhs_cli->pce_opts.pce_name) + == 0) { + return pce_rhs_cli; + } + } + } + + return NULL; +} + +/* Add a new pce_opts_cli to pcep_g, return false if MAX_PCES, true otherwise */ +static bool pcep_cli_add_pce(struct pce_opts_cli *pce_opts_cli) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] == NULL) { + pcep_g->pce_opts_cli[i] = pce_opts_cli; + pcep_g->num_pce_opts_cli++; + return true; + } + } + + return false; +} + +/* Create a new pce opts_cli */ +static struct pce_opts_cli *pcep_cli_create_pce_opts(const char *name) +{ + struct pce_opts_cli *pce_opts_cli = + XCALLOC(MTYPE_PCEP, sizeof(struct pce_opts_cli)); + strlcpy(pce_opts_cli->pce_opts.pce_name, name, + sizeof(pce_opts_cli->pce_opts.pce_name)); + pce_opts_cli->pce_opts.port = PCEP_DEFAULT_PORT; + + return pce_opts_cli; +} + +static void pcep_cli_delete_pce(const char *pce_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] != NULL) { + if (strcmp(pcep_g->pce_opts_cli[i]->pce_opts.pce_name, + pce_name) + == 0) { + XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]); + pcep_g->pce_opts_cli[i] = NULL; + pcep_g->num_pce_opts_cli--; + return; + } + } + } +} + +static void +pcep_cli_merge_pcep_pce_config_options(struct pce_opts_cli *pce_opts_cli) +{ + if (pce_opts_cli->merged == true) { + return; + } + + struct pcep_config_group_opts *pce_config = + pcep_cli_find_pcep_pce_config(pce_opts_cli->config_group_name); + + /* Configuration priorities: + * 1) pce_opts->config_opts, if present, overwrite pce_config + * config_opts 2) pce_config config_opts, if present, overwrite + * default config_opts 3) If neither pce_opts->config_opts nor + * pce_config config_opts are set, then the default config_opts value + * will be used. + */ + + const char *tcp_md5_auth_str = + pce_opts_cli->pce_config_group_opts.tcp_md5_auth; + if (pce_opts_cli->pce_config_group_opts.tcp_md5_auth[0] == '\0') { + if (pce_config != NULL && pce_config->tcp_md5_auth[0] != '\0') { + tcp_md5_auth_str = pce_config->tcp_md5_auth; + } else { + tcp_md5_auth_str = + default_pcep_config_group_opts_g.tcp_md5_auth; + } + } + strncpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth, + tcp_md5_auth_str, TCP_MD5SIG_MAXKEYLEN); + + struct ipaddr *source_ip = + &pce_opts_cli->pce_config_group_opts.source_ip; + if (pce_opts_cli->pce_config_group_opts.source_ip.ipa_type + == IPADDR_NONE) { + if (pce_config != NULL + && pce_config->source_ip.ipa_type != IPADDR_NONE) { + source_ip = &pce_config->source_ip; + } else { + source_ip = &default_pcep_config_group_opts_g.source_ip; + } + } + memcpy(&pce_opts_cli->pce_opts.config_opts.source_ip, source_ip, + sizeof(struct ipaddr)); + + MERGE_COMPARE_CONFIG_GROUP_VALUE(draft07, false); + MERGE_COMPARE_CONFIG_GROUP_VALUE(pce_initiated, false); + MERGE_COMPARE_CONFIG_GROUP_VALUE(keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(min_keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(max_keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(min_dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(max_dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(pcep_request_time_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(session_timeout_inteval_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(delegation_timeout_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(source_port, 0); + + pce_opts_cli->merged = true; +} + +/* Check if a pcep_config_group_opts already exists based on its name and return + * it, return NULL otherwise */ +static struct pcep_config_group_opts * +pcep_cli_find_pcep_pce_config(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + struct pcep_config_group_opts *pcep_pce_config_rhs = + pcep_g->config_group_opts[i]; + if (pcep_pce_config_rhs != NULL) { + if (strcmp(group_name, pcep_pce_config_rhs->name) + == 0) { + return pcep_pce_config_rhs; + } + } + } + + return NULL; +} + +/* Add a new pcep_config_group_opts to pcep_g, return false if MAX_PCE, + * true otherwise */ +static bool pcep_cli_add_pcep_pce_config( + struct pcep_config_group_opts *pcep_config_group_opts) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->config_group_opts[i] == NULL) { + pcep_g->config_group_opts[i] = pcep_config_group_opts; + pcep_g->num_config_group_opts++; + return true; + } + } + + return false; +} + +/* Create a new pce group, inheriting its values from the default pce group */ +static struct pcep_config_group_opts * +pcep_cli_create_pcep_pce_config(const char *group_name) +{ + struct pcep_config_group_opts *pcep_config_group_opts = + XCALLOC(MTYPE_PCEP, sizeof(struct pcep_config_group_opts)); + strlcpy(pcep_config_group_opts->name, group_name, + sizeof(pcep_config_group_opts->name)); + + return pcep_config_group_opts; +} + +/* Iterate the pce_opts and return true if the pce-group-name is referenced, + * false otherwise. */ +static bool pcep_cli_is_pcep_pce_config_used(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] != NULL) { + if (strcmp(pcep_g->pce_opts_cli[i]->config_group_name, + group_name) + == 0) { + return true; + } + } + } + + return false; +} + +static void pcep_cli_delete_pcep_pce_config(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->config_group_opts[i] != NULL) { + if (strcmp(pcep_g->config_group_opts[i]->name, + group_name) + == 0) { + XFREE(MTYPE_PCEP, pcep_g->config_group_opts[i]); + pcep_g->config_group_opts[i] = NULL; + pcep_g->num_config_group_opts--; + return; + } + } + } +} + +static bool pcep_cli_pcc_has_pce(const char *pce_name) +{ + for (int i = 0; i < MAX_PCC; i++) { + struct pce_opts *pce_opts = pce_connections_g.connections[i]; + if (pce_opts == NULL) { + continue; + } + + if (strcmp(pce_opts->pce_name, pce_name) == 0) { + return true; + } + } + + return false; +} + +static void pcep_cli_add_pce_connection(struct pce_opts *pce_opts) +{ + for (int i = 0; i < MAX_PCC; i++) { + if (pce_connections_g.connections[i] == NULL) { + pce_connections_g.num_connections++; + pce_connections_g.connections[i] = pce_opts; + return; + } + } +} + +static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts) +{ + for (int i = 0; i < MAX_PCC; i++) { + if (pce_connections_g.connections[i] == pce_opts) { + pce_connections_g.num_connections--; + pce_connections_g.connections[i] = NULL; + return; + } + } +} + +/* + * VTY command implementations + */ + +static int path_pcep_cli_debug(struct vty *vty, const char *no_str, + const char *basic_str, const char *path_str, + const char *message_str, const char *pceplib_str) +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + bool no = (no_str != NULL); + + DEBUG_MODE_SET(&pcep_g->dbg, mode, !no); + + if (basic_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC, !no); + } + if (path_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH, !no); + } + if (message_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP, !no); + } + if (pceplib_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB, !no); + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_show_srte_pcep_counters(struct vty *vty) +{ + int i, j, row; + time_t diff_time; + struct tm *tm_info; + char tm_buffer[26]; + struct counters_group *group; + struct counters_subgroup *subgroup; + struct counter *counter; + const char *group_name, *empty_string = ""; + struct ttable *tt; + char *table; + + group = pcep_ctrl_get_counters(pcep_g->fpt, 1); + + if (group == NULL) { + vty_out(vty, "No counters to display.\n\n"); + return CMD_SUCCESS; + } + + diff_time = time(NULL) - group->start_time; + tm_info = localtime(&group->start_time); + strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info); + + vty_out(vty, "PCEP counters since %s (%luh %lum %lus):\n", tm_buffer, + diff_time / 3600, (diff_time / 60) % 60, diff_time % 60); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Group|Name|Value"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (row = 0, i = 0; i <= group->num_subgroups; i++) { + subgroup = group->subgroups[i]; + if (subgroup != NULL) { + group_name = subgroup->counters_subgroup_name; + for (j = 0; j <= subgroup->num_counters; j++) { + counter = subgroup->counters[j]; + if (counter != NULL) { + ttable_add_row(tt, "%s|%s|%u", + group_name, + counter->counter_name, + counter->counter_value); + row++; + group_name = empty_string; + } + } + ttable_rowseps(tt, row, BOTTOM, true, '-'); + } + } + + /* Dump the generated table. */ + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + + ttable_del(tt); + + pcep_lib_free_counters(group); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcep_pce_config(struct vty *vty, + const char *pcep_pce_config) +{ + struct pcep_config_group_opts *pce_config = + pcep_cli_find_pcep_pce_config(pcep_pce_config); + if (pce_config == NULL) { + pce_config = pcep_cli_create_pcep_pce_config(pcep_pce_config); + if (pcep_cli_add_pcep_pce_config(pce_config) == false) { + vty_out(vty, + "%% Cannot create pce-config, as the Maximum limit of %d pce-config has been reached.\n", + MAX_PCE); + XFREE(MTYPE_PCEP, pce_config); + return CMD_WARNING; + } + } else { + vty_out(vty, + "Notice: changes to this pce-config will not affect PCEs already configured with this group\n"); + } + + current_pcep_config_group_opts_g = pce_config; + vty->node = PCEP_PCE_CONFIG_NODE; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcep_pce_config_delete(struct vty *vty, + const char *pcep_pce_config) +{ + struct pcep_config_group_opts *pce_config = + pcep_cli_find_pcep_pce_config(pcep_pce_config); + if (pce_config == NULL) { + vty_out(vty, + "%% Cannot delete pce-config, since it does not exist.\n"); + return CMD_WARNING; + } + + if (pcep_cli_is_pcep_pce_config_used(pce_config->name)) { + vty_out(vty, + "%% Cannot delete pce-config, since it is in use by a peer.\n"); + return CMD_WARNING; + } + + pcep_cli_delete_pcep_pce_config(pce_config->name); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_show_srte_pcep_pce_config(struct vty *vty, + const char *pcep_pce_config) +{ + char buf[1024] = ""; + + /* Only show 1 Peer config group */ + struct pcep_config_group_opts *group_opts; + if (pcep_pce_config != NULL) { + if (strcmp(pcep_pce_config, "default") == 0) { + group_opts = &default_pcep_config_group_opts_g; + } else { + group_opts = + pcep_cli_find_pcep_pce_config(pcep_pce_config); + } + if (group_opts == NULL) { + vty_out(vty, "%% pce-config [%s] does not exist.\n", + pcep_pce_config); + return CMD_WARNING; + } + + vty_out(vty, "pce-config: %s\n", group_opts->name); + pcep_cli_print_pce_config(group_opts, buf, sizeof(buf)); + vty_out(vty, "%s", buf); + return CMD_SUCCESS; + } + + /* Show all Peer config groups */ + for (int i = 0; i < MAX_PCE; i++) { + group_opts = pcep_g->config_group_opts[i]; + if (group_opts == NULL) { + continue; + } + + vty_out(vty, "pce-config: %s\n", group_opts->name); + pcep_cli_print_pce_config(group_opts, buf, sizeof(buf)); + vty_out(vty, "%s", buf); + buf[0] = 0; + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pce(struct vty *vty, const char *pce_peer_name) +{ + /* If it already exists, it will be updated in the sub-commands */ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(pce_peer_name); + if (pce_opts_cli == NULL) { + pce_opts_cli = pcep_cli_create_pce_opts(pce_peer_name); + + if (!pcep_cli_add_pce(pce_opts_cli)) { + vty_out(vty, + "%% Cannot create PCE, as the Maximum limit of %d PCEs has been reached.\n", + MAX_PCE); + XFREE(MTYPE_PCEP, pce_opts_cli); + return CMD_WARNING; + } + } + + current_pce_opts_g = pce_opts_cli; + vty->node = PCEP_PCE_NODE; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pce_delete(struct vty *vty, const char *pce_peer_name) +{ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(pce_peer_name); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCC peer does not exist.\n"); + return CMD_WARNING; + } + + /* To better work with frr-reload, go ahead and delete it if its in use + */ + if (pcep_cli_pcc_has_pce(pce_peer_name)) { + vty_out(vty, + "%% Notice: the pce is in use by a PCC, also disconnecting.\n"); + path_pcep_cli_pcc_pcc_peer_delete(vty, pce_peer_name, NULL, 0); + } + + pcep_cli_delete_pce(pce_peer_name); + + return CMD_SUCCESS; +} + +/* Internal Util func to show an individual PCE, + * only used by path_pcep_cli_show_srte_pcep_pce() */ +static void show_pce_peer(struct vty *vty, struct pce_opts_cli *pce_opts_cli) +{ + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + vty_out(vty, "PCE: %s\n", pce_opts->pce_name); + + /* Remote PCE IP address */ + if (IS_IPADDR_V6(&pce_opts->addr)) { + vty_out(vty, " %s %s %pI6 %s %d\n", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IPV6, &pce_opts->addr.ipaddr_v6, + PCEP_VTYSH_ARG_PORT, pce_opts->port); + } else { + vty_out(vty, " %s %s %pI4 %s %d\n", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IP, &pce_opts->addr.ipaddr_v4, + PCEP_VTYSH_ARG_PORT, pce_opts->port); + } + + if (pce_opts_cli->config_group_name[0] != '\0') { + vty_out(vty, " pce-config: %s\n", + pce_opts_cli->config_group_name); + } + + char buf[1024] = ""; + pcep_cli_print_pce_config(&pce_opts->config_opts, buf, sizeof(buf)); + vty_out(vty, "%s", buf); +} + +static int path_pcep_cli_show_srte_pcep_pce(struct vty *vty, + const char *pce_peer) +{ + /* Only show 1 PCE */ + struct pce_opts_cli *pce_opts_cli; + if (pce_peer != NULL) { + pce_opts_cli = pcep_cli_find_pce(pce_peer); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", pce_peer); + return CMD_WARNING; + } + + pcep_cli_merge_pcep_pce_config_options(pce_opts_cli); + show_pce_peer(vty, pce_opts_cli); + + return CMD_SUCCESS; + } + + /* Show all PCEs */ + for (int i = 0; i < MAX_PCE; i++) { + pce_opts_cli = pcep_g->pce_opts_cli[i]; + if (pce_opts_cli == NULL) { + continue; + } + + pcep_cli_merge_pcep_pce_config_options(pce_opts_cli); + show_pce_peer(vty, pce_opts_cli); + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_sr_draft07(struct vty *vty) +{ + struct pcep_config_group_opts *pce_config = NULL; + + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_config = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_PCE_CONFIG_NODE) { + pce_config = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + pce_config->draft07 = true; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_pce_initiated(struct vty *vty) +{ + struct pcep_config_group_opts *pce_config = NULL; + + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_config = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_PCE_CONFIG_NODE) { + pce_config = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + pce_config->pce_initiated = true; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty, + const char *tcp_md5_auth) +{ + struct pcep_config_group_opts *pce_config = NULL; + + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_config = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_PCE_CONFIG_NODE) { + pce_config = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + strncpy(pce_config->tcp_md5_auth, tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_address(struct vty *vty, const char *ip_str, + struct in_addr *ip, const char *ipv6_str, + struct in6_addr *ipv6, + const char *port_str, long port) +{ + struct pce_opts *pce_opts = NULL; + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_opts = ¤t_pce_opts_g->pce_opts; + current_pce_opts_g->merged = false; + } else { + return CMD_ERR_NO_MATCH; + } + + if (ipv6_str != NULL) { + pce_opts->addr.ipa_type = IPADDR_V6; + memcpy(&pce_opts->addr.ipaddr_v6, ipv6, + sizeof(struct in6_addr)); + } else if (ip_str != NULL) { + pce_opts->addr.ipa_type = IPADDR_V4; + memcpy(&pce_opts->addr.ipaddr_v4, ip, sizeof(struct in_addr)); + } else { + return CMD_ERR_NO_MATCH; + } + + /* Handle the optional port */ + pce_opts->port = PCEP_DEFAULT_PORT; + PCEP_VTYSH_INT_ARG_CHECK(port_str, port, pce_opts->port, 0, 65535); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_source_address(struct vty *vty, + const char *ip_str, + struct in_addr *ip, + const char *ipv6_str, + struct in6_addr *ipv6, + const char *port_str, long port) +{ + struct pcep_config_group_opts *pce_config = NULL; + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_config = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_PCE_CONFIG_NODE) { + pce_config = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + /* Handle the optional source IP */ + if (ipv6_str != NULL) { + pce_config->source_ip.ipa_type = IPADDR_V6; + memcpy(&pce_config->source_ip.ipaddr_v6, ipv6, + sizeof(struct in6_addr)); + } else if (ip_str != NULL) { + pce_config->source_ip.ipa_type = IPADDR_V4; + memcpy(&pce_config->source_ip.ipaddr_v4, ip, + sizeof(struct in_addr)); + } + + /* Handle the optional port */ + PCEP_VTYSH_INT_ARG_CHECK(port_str, port, pce_config->source_port, 0, + 65535); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_pcep_pce_config_ref(struct vty *vty, + const char *config_group_name) +{ + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + current_pce_opts_g->merged = false; + } else { + return CMD_ERR_NO_MATCH; + } + + struct pcep_config_group_opts *pce_config = + pcep_cli_find_pcep_pce_config(config_group_name); + if (pce_config == NULL) { + vty_out(vty, "%% pce-config [%s] does not exist.\n", + config_group_name); + return CMD_WARNING; + } + + strlcpy(current_pce_opts_g->config_group_name, config_group_name, + sizeof(current_pce_opts_g->config_group_name)); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_timers( + struct vty *vty, const char *keep_alive_str, long keep_alive, + const char *min_peer_keep_alive_str, long min_peer_keep_alive, + const char *max_peer_keep_alive_str, long max_peer_keep_alive, + const char *dead_timer_str, long dead_timer, + const char *min_peer_dead_timer_str, long min_peer_dead_timer, + const char *max_peer_dead_timer_str, long max_peer_dead_timer, + const char *pcep_request_str, long pcep_request, + const char *session_timeout_interval_str, long session_timeout_interval, + const char *delegation_timeout_str, long delegation_timeout) +{ + struct pcep_config_group_opts *pce_config = NULL; + if (vty->node == PCEP_PCE_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_config = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_PCE_CONFIG_NODE) { + pce_config = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + if (min_peer_keep_alive && max_peer_keep_alive) + if (min_peer_keep_alive >= max_peer_keep_alive) { + return CMD_ERR_NO_MATCH; + } + + if (min_peer_dead_timer && max_peer_dead_timer) + if (min_peer_dead_timer >= max_peer_dead_timer) { + return CMD_ERR_NO_MATCH; + } + + /* Handle the arguments */ + PCEP_VTYSH_INT_ARG_CHECK(keep_alive_str, keep_alive, + pce_config->keep_alive_seconds, 0, 64); + PCEP_VTYSH_INT_ARG_CHECK(min_peer_keep_alive_str, min_peer_keep_alive, + pce_config->min_keep_alive_seconds, 0, 256); + PCEP_VTYSH_INT_ARG_CHECK(max_peer_keep_alive_str, max_peer_keep_alive, + pce_config->max_keep_alive_seconds, 0, 256); + PCEP_VTYSH_INT_ARG_CHECK(dead_timer_str, dead_timer, + pce_config->dead_timer_seconds, 3, 256); + PCEP_VTYSH_INT_ARG_CHECK(min_peer_dead_timer_str, min_peer_dead_timer, + pce_config->min_dead_timer_seconds, 3, 256); + PCEP_VTYSH_INT_ARG_CHECK(max_peer_dead_timer_str, max_peer_dead_timer, + pce_config->max_dead_timer_seconds, 3, 256); + PCEP_VTYSH_INT_ARG_CHECK(pcep_request_str, pcep_request, + pce_config->pcep_request_time_seconds, 0, 121); + PCEP_VTYSH_INT_ARG_CHECK( + session_timeout_interval_str, session_timeout_interval, + pce_config->session_timeout_inteval_seconds, 0, 121); + PCEP_VTYSH_INT_ARG_CHECK(delegation_timeout_str, delegation_timeout, + pce_config->delegation_timeout_seconds, 0, 61); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc(struct vty *vty) +{ + VTY_PUSH_CONTEXT_NULL(PCEP_PCC_NODE); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_delete(struct vty *vty) +{ + /* Clear the pce_connections */ + memset(&pce_connections_g, 0, sizeof(pce_connections_g)); + pcc_msd_configured_g = false; + + pcep_ctrl_remove_pcc(pcep_g->fpt, NULL); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_pcc_msd(struct vty *vty, const char *msd_str, + long msd) +{ + pcc_msd_configured_g = true; + PCEP_VTYSH_INT_ARG_CHECK(msd_str, msd, pcc_msd_g, 0, 33); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_pcc_peer(struct vty *vty, const char *peer_name, + const char *precedence_str, + long precedence) +{ + /* Check if the pcc-peer exists */ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", peer_name); + return CMD_WARNING; + } + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + + /* Check if the pcc-peer is duplicated */ + if (pcep_cli_pcc_has_pce(peer_name)) { + vty_out(vty, "%% The peer [%s] has already been configured.\n", + peer_name); + return CMD_WARNING; + } + + /* Get the optional precedence argument */ + pce_opts->precedence = DEFAULT_PCE_PRECEDENCE; + PCEP_VTYSH_INT_ARG_CHECK(precedence_str, precedence, + pce_opts->precedence, 0, 256); + + /* Finalize the pce_opts config values */ + pcep_cli_merge_pcep_pce_config_options(pce_opts_cli); + pcep_cli_add_pce_connection(&pce_opts_cli->pce_opts); + + /* Verify the PCE has the IP set */ + struct in6_addr zero_v6_addr; + memset(&zero_v6_addr, 0, sizeof(struct in6_addr)); + if (memcmp(&pce_opts->addr.ip, &zero_v6_addr, IPADDRSZ(&pce_opts->addr)) + == 0) { + vty_out(vty, + "%% The peer [%s] does not have an IP set and cannot be used until it does.\n", + peer_name); + return CMD_WARNING; + } + + /* Update the pcc_opts with the source ip, port, and msd */ + struct pcc_opts *pcc_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pcc_opts)); + memcpy(&pcc_opts_copy->addr, + &pce_opts_cli->pce_opts.config_opts.source_ip, + sizeof(struct pcc_opts)); + pcc_opts_copy->msd = pcc_msd_g; + pcc_opts_copy->port = pce_opts_cli->pce_opts.config_opts.source_port; + if (pcep_ctrl_update_pcc_options(pcep_g->fpt, pcc_opts_copy)) { + return CMD_WARNING; + } + + /* Send a copy of the pce_opts, this one is only used for the CLI */ + struct pce_opts *pce_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts)); + memcpy(pce_opts_copy, pce_opts, sizeof(struct pce_opts)); + if (pcep_ctrl_update_pce_options(pcep_g->fpt, pce_opts_copy)) { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_pcc_peer_delete(struct vty *vty, + const char *peer_name, + const char *precedence_str, + long precedence) +{ + /* Check if the pcc-peer is connected to the PCC */ + if (!pcep_cli_pcc_has_pce(peer_name)) { + vty_out(vty, + "%% WARN: The peer [%s] is not connected to the PCC.\n", + peer_name); + return CMD_WARNING; + } + + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name); + pcep_cli_remove_pce_connection(&pce_opts_cli->pce_opts); + + /* Send a copy of the pce_opts, this one is used for CLI only */ + struct pce_opts *pce_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts)); + memcpy(pce_opts_copy, &pce_opts_cli->pce_opts, sizeof(struct pce_opts)); + pcep_ctrl_remove_pcc(pcep_g->fpt, pce_opts_copy); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_show_srte_pcep_pcc(struct vty *vty) +{ + vty_out(vty, "pcc msd %d\n", pcc_msd_g); + + return CMD_SUCCESS; +} + +/* Internal util function to print pcep capabilities to a buffer */ +static void print_pcep_capabilities(char *buf, size_t buf_len, + pcep_configuration *config) +{ + if (config->support_stateful_pce_lsp_update) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_STATEFUL); + } + if (config->support_include_db_version) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_INCL_DB_VER); + } + if (config->support_lsp_triggered_resync) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_LSP_TRIGGERED); + } + if (config->support_lsp_delta_sync) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_LSP_DELTA); + } + if (config->support_pce_triggered_initial_sync) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_PCE_TRIGGERED); + } + if (config->support_sr_te_pst) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_SR_TE_PST); + } + if (config->pcc_can_resolve_nai_to_sid) { + csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_PCC_RESOLVE_NAI); + } +} + +/* Internal util function to print a pcep session */ +static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts, + struct pcep_pcc_info *pcc_info) +{ + char buf[1024]; + buf[0] = '\0'; + + vty_out(vty, "\nPCE %s\n", pce_opts->pce_name); + + /* PCE IP */ + if (IS_IPADDR_V4(&pce_opts->addr)) { + vty_out(vty, " PCE IP %pI4 port %d\n", + &pce_opts->addr.ipaddr_v4, pce_opts->port); + } else if (IS_IPADDR_V6(&pce_opts->addr)) { + vty_out(vty, " PCE IPv6 %pI6 port %d\n", + &pce_opts->addr.ipaddr_v6, pce_opts->port); + } + + /* PCC IP */ + if (IS_IPADDR_V4(&pcc_info->pcc_addr)) { + vty_out(vty, " PCC IP %pI4 port %d\n", + &pcc_info->pcc_addr.ipaddr_v4, pcc_info->pcc_port); + } else if (IS_IPADDR_V6(&pcc_info->pcc_addr)) { + vty_out(vty, " PCC IPv6 %pI6 port %d\n", + &pcc_info->pcc_addr.ipaddr_v6, pcc_info->pcc_port); + } + vty_out(vty, " PCC MSD %d\n", pcc_info->msd); + + if (pcc_info->status == PCEP_PCC_OPERATING) { + vty_out(vty, " Session Status UP\n"); + } else { + vty_out(vty, " Session Status %s\n", + pcc_status_name(pcc_info->status)); + } + + if (pcc_info->is_best_multi_pce) { + vty_out(vty, " MultiPCE precedence %d, best candidate\n", + ((pcc_info->precedence > 0) ? pcc_info->precedence + : DEFAULT_PCE_PRECEDENCE)); + } else { + vty_out(vty, " MultiPCE precedence %d\n", + ((pcc_info->precedence > 0) ? pcc_info->precedence + : DEFAULT_PCE_PRECEDENCE)); + } + + /* PCEPlib pcep session values, get a thread safe copy of the counters + */ + pcep_session *session = + pcep_ctrl_get_pcep_session(pcep_g->fpt, pcc_info->pcc_id); + + /* Config Options values */ + struct pcep_config_group_opts *config_opts = &pce_opts->config_opts; + if (session != NULL) { + vty_out(vty, " Timer: KeepAlive config %d, pce-negotiated %d\n", + config_opts->keep_alive_seconds, + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds); + vty_out(vty, " Timer: DeadTimer config %d, pce-negotiated %d\n", + config_opts->dead_timer_seconds, + session->pcc_config.dead_timer_pce_negotiated_seconds); + } else { + vty_out(vty, " Timer: KeepAlive %d\n", + config_opts->keep_alive_seconds); + vty_out(vty, " Timer: DeadTimer %d\n", + config_opts->dead_timer_seconds); + } + vty_out(vty, " Timer: PcRequest %d\n", + config_opts->pcep_request_time_seconds); + vty_out(vty, " Timer: SessionTimeout Interval %d\n", + config_opts->session_timeout_inteval_seconds); + vty_out(vty, " Timer: Delegation Timeout %d\n", + config_opts->delegation_timeout_seconds); + if (strlen(config_opts->tcp_md5_auth) > 0) { + vty_out(vty, " TCP MD5 Auth Str: %s\n", + config_opts->tcp_md5_auth); + } else { + vty_out(vty, " No TCP MD5 Auth\n"); + } + + if (config_opts->draft07) { + vty_out(vty, " PCE SR Version draft07\n"); + } else { + vty_out(vty, " PCE SR Version draft16 and RFC8408\n"); + } + + vty_out(vty, " Next PcReq ID %d\n", pcc_info->next_reqid); + vty_out(vty, " Next PLSP ID %d\n", pcc_info->next_plspid); + + if (session != NULL) { + if (pcc_info->status == PCEP_PCC_SYNCHRONIZING + || pcc_info->status == PCEP_PCC_OPERATING) { + time_t current_time = time(NULL); + struct tm lt = {0}; + /* Just for the timezone */ + localtime_r(¤t_time, <); + gmtime_r(&session->time_connected, <); + vty_out(vty, + " Connected for %ld seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n", + (current_time - session->time_connected), + lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, + lt.tm_hour, lt.tm_min, lt.tm_sec); + } + + /* PCC capabilities */ + buf[0] = '\0'; + int index = 0; + if (config_opts->pce_initiated) { + index += csnprintfrr(buf, sizeof(buf), "%s", + PCEP_CLI_CAP_PCC_PCE_INITIATED); + } else { + index += csnprintfrr(buf, sizeof(buf), "%s", + PCEP_CLI_CAP_PCC_INITIATED); + } + print_pcep_capabilities(buf, sizeof(buf) - index, + &session->pcc_config); + vty_out(vty, " PCC Capabilities:%s\n", buf); + + /* PCE capabilities */ + buf[0] = '\0'; + print_pcep_capabilities(buf, sizeof(buf), &session->pce_config); + if (buf[0] != '\0') { + vty_out(vty, " PCE Capabilities:%s\n", buf); + } + XFREE(MTYPE_PCEP, session); + } else { + vty_out(vty, " Detailed session information not available\n"); + } + + /* Message Counters, get a thread safe copy of the counters */ + struct counters_group *group = + pcep_ctrl_get_counters(pcep_g->fpt, pcc_info->pcc_id); + + if (group != NULL) { + struct counters_subgroup *rx_msgs = + find_subgroup(group, COUNTER_SUBGROUP_ID_RX_MSG); + struct counters_subgroup *tx_msgs = + find_subgroup(group, COUNTER_SUBGROUP_ID_TX_MSG); + + if (rx_msgs != NULL && tx_msgs != NULL) { + vty_out(vty, " PCEP Message Statistics\n"); + vty_out(vty, " %27s %6s\n", "Sent", "Rcvd"); + for (int i = 0; i < rx_msgs->max_counters; i++) { + struct counter *rx_counter = + rx_msgs->counters[i]; + struct counter *tx_counter = + tx_msgs->counters[i]; + if (rx_counter != NULL && tx_counter != NULL) { + vty_out(vty, " %20s: %5d %5d\n", + tx_counter->counter_name, + tx_counter->counter_value, + rx_counter->counter_value); + } + } + vty_out(vty, " %20s: %5d %5d\n", "Total", + subgroup_counters_total(tx_msgs), + subgroup_counters_total(rx_msgs)); + } + pcep_lib_free_counters(group); + } else { + vty_out(vty, " Counters not available\n"); + } + + XFREE(MTYPE_PCEP, pcc_info); +} + +static int path_pcep_cli_show_srte_pcep_session(struct vty *vty, + const char *pcc_peer) +{ + struct pce_opts_cli *pce_opts_cli; + struct pcep_pcc_info *pcc_info; + + /* Only show 1 PCEP session */ + if (pcc_peer != NULL) { + pce_opts_cli = pcep_cli_find_pce(pcc_peer); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", pcc_peer); + return CMD_WARNING; + } + + if (!pcep_cli_pcc_has_pce(pcc_peer)) { + vty_out(vty, "%% PCC is not connected to PCE [%s].\n", + pcc_peer); + return CMD_WARNING; + } + + pcc_info = pcep_ctrl_get_pcc_info(pcep_g->fpt, pcc_peer); + if (pcc_info == NULL) { + vty_out(vty, + "%% Cannot retrieve PCEP session info for PCE [%s]\n", + pcc_peer); + return CMD_WARNING; + } + + print_pcep_session(vty, &pce_opts_cli->pce_opts, pcc_info); + + return CMD_SUCCESS; + } + + /* Show all PCEP sessions */ + struct pce_opts *pce_opts; + int num_pcep_sessions_conf = 0; + int num_pcep_sessions_conn = 0; + for (int i = 0; i < MAX_PCC; i++) { + pce_opts = pce_connections_g.connections[i]; + if (pce_opts == NULL) { + continue; + } + + pcc_info = + pcep_ctrl_get_pcc_info(pcep_g->fpt, pce_opts->pce_name); + if (pcc_info == NULL) { + vty_out(vty, + "%% Cannot retrieve PCEP session info for PCE [%s]\n", + pce_opts->pce_name); + continue; + } + + num_pcep_sessions_conn += + pcc_info->status == PCEP_PCC_OPERATING ? 1 : 0; + num_pcep_sessions_conf++; + print_pcep_session(vty, pce_opts, pcc_info); + } + + vty_out(vty, "PCEP Sessions => Configured %d ; Connected %d\n", + num_pcep_sessions_conf, num_pcep_sessions_conn); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_clear_srte_pcep_session(struct vty *vty, + const char *pcc_peer) +{ + struct pce_opts_cli *pce_opts_cli; + + /* Only clear 1 PCEP session */ + if (pcc_peer != NULL) { + pce_opts_cli = pcep_cli_find_pce(pcc_peer); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", pcc_peer); + return CMD_WARNING; + } + + if (!pcep_cli_pcc_has_pce(pcc_peer)) { + vty_out(vty, "%% PCC is not connected to PCE [%s].\n", + pcc_peer); + return CMD_WARNING; + } + + pcep_ctrl_reset_pcc_session(pcep_g->fpt, + pce_opts_cli->pce_opts.pce_name); + vty_out(vty, "PCEP session cleared for peer %s\n", pcc_peer); + + return CMD_SUCCESS; + } + + /* Clear all PCEP sessions */ + struct pce_opts *pce_opts; + int num_pcep_sessions = 0; + for (int i = 0; i < MAX_PCC; i++) { + pce_opts = pce_connections_g.connections[i]; + if (pce_opts == NULL) { + continue; + } + + num_pcep_sessions++; + pcep_ctrl_reset_pcc_session(pcep_g->fpt, pce_opts->pce_name); + vty_out(vty, "PCEP session cleared for peer %s\n", + pce_opts->pce_name); + } + + vty_out(vty, "Cleared [%d] PCEP sessions\n", num_pcep_sessions); + + return CMD_SUCCESS; +} + +/* + * Config Write functions + */ + +int pcep_cli_debug_config_write(struct vty *vty) +{ + char buff[128] = ""; + + if (DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_CONF)) { + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_BASIC); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_PATH); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_MESSAGE); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_PCEPLIB); + vty_out(vty, "debug pathd pcep%s\n", buff); + buff[0] = 0; + return 1; + } + + return 0; +} + +int pcep_cli_debug_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&pcep_g->dbg, flags, set); + + /* If all modes have been turned off, don't preserve options. */ + if (!DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_ALL)) + DEBUG_CLEAR(&pcep_g->dbg); + + return 0; +} + +int pcep_cli_pcep_config_write(struct vty *vty) +{ + vty_out(vty, " pcep\n"); + return 1; +} + +int pcep_cli_pcc_config_write(struct vty *vty) +{ + struct pce_opts *pce_opts; + char buf[128] = ""; + int lines = 0; + + /* The MSD, nor any PCE peers have been configured on the PCC */ + if (!pcc_msd_configured_g && pce_connections_g.num_connections == 0) { + return lines; + } + + vty_out(vty, " pcc\n"); + lines++; + + /* Prepare the MSD, if present */ + if (pcc_msd_configured_g) { + vty_out(vty, " %s %d\n", PCEP_VTYSH_ARG_MSD, pcc_msd_g); + lines++; + } + + if (pce_connections_g.num_connections == 0) { + return lines; + } + + buf[0] = 0; + for (int i = 0; i < MAX_PCC; i++) { + pce_opts = pce_connections_g.connections[i]; + if (pce_opts == NULL) { + continue; + } + + /* Only show the PCEs configured in the pcc sub-command */ + if (!pcep_cli_pcc_has_pce(pce_opts->pce_name)) { + continue; + } + + csnprintfrr(buf, sizeof(buf), " peer %s", + pce_opts->pce_name); + if (pce_opts->precedence > 0) { + csnprintfrr(buf, sizeof(buf), " %s %d", + PCEP_VTYSH_ARG_PRECEDENCE, + pce_opts->precedence); + } + vty_out(vty, "%s\n", buf); + lines++; + buf[0] = 0; + } + + return lines; +} + +/* Internal function used by pcep_cli_pce_config_write() + * and pcep_cli_pcep_pce_config_write() */ +static int pcep_cli_print_pce_config(struct pcep_config_group_opts *group_opts, + char *buf, size_t buf_len) +{ + int lines = 0; + + if (group_opts->source_ip.ipa_type != IPADDR_NONE + || group_opts->source_port != 0) { + csnprintfrr(buf, buf_len, " "); + if (IS_IPADDR_V4(&group_opts->source_ip)) { + csnprintfrr(buf, buf_len, " %s %s %pI4", + PCEP_VTYSH_ARG_SOURCE_ADDRESS, + PCEP_VTYSH_ARG_IP, + &group_opts->source_ip.ipaddr_v4); + } else if (IS_IPADDR_V6(&group_opts->source_ip)) { + csnprintfrr(buf, buf_len, " %s %s %pI6", + PCEP_VTYSH_ARG_SOURCE_ADDRESS, + PCEP_VTYSH_ARG_IPV6, + &group_opts->source_ip.ipaddr_v6); + } + if (group_opts->source_port > 0) { + csnprintfrr(buf, buf_len, " %s %d", PCEP_VTYSH_ARG_PORT, + group_opts->source_port); + } + csnprintfrr(buf, buf_len, "\n"); + lines++; + } + /* Group the keep-alive together for devman */ + if ((group_opts->keep_alive_seconds > 0) + || (group_opts->min_keep_alive_seconds > 0) + || (group_opts->max_keep_alive_seconds > 0)) { + csnprintfrr(buf, buf_len, " %s", PCEP_VTYSH_ARG_TIMER); + + if (group_opts->keep_alive_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_KEEP_ALIVE, + group_opts->keep_alive_seconds); + } + if (group_opts->min_keep_alive_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_KEEP_ALIVE_MIN, + group_opts->min_keep_alive_seconds); + } + if (group_opts->max_keep_alive_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_KEEP_ALIVE_MAX, + group_opts->max_keep_alive_seconds); + } + csnprintfrr(buf, buf_len, "\n"); + lines++; + } + + /* Group the dead-timer together for devman */ + if ((group_opts->dead_timer_seconds > 0) + || (group_opts->min_dead_timer_seconds > 0) + || (group_opts->max_dead_timer_seconds > 0)) { + csnprintfrr(buf, buf_len, " %s", PCEP_VTYSH_ARG_TIMER); + + if (group_opts->dead_timer_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_DEAD_TIMER, + group_opts->dead_timer_seconds); + } + if (group_opts->min_dead_timer_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_DEAD_TIMER_MIN, + group_opts->min_dead_timer_seconds); + } + if (group_opts->max_dead_timer_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %d", + PCEP_VTYSH_ARG_DEAD_TIMER_MAX, + group_opts->max_dead_timer_seconds); + } + csnprintfrr(buf, buf_len, "\n"); + lines++; + } + + if (group_opts->pcep_request_time_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %s %d\n", + PCEP_VTYSH_ARG_TIMER, PCEP_VTYSH_ARG_PCEP_REQUEST, + group_opts->pcep_request_time_seconds); + lines++; + } + if (group_opts->delegation_timeout_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %s %d\n", + PCEP_VTYSH_ARG_TIMER, + PCEP_VTYSH_ARG_DELEGATION_TIMEOUT, + group_opts->delegation_timeout_seconds); + lines++; + } + if (group_opts->session_timeout_inteval_seconds > 0) { + csnprintfrr(buf, buf_len, " %s %s %d\n", + PCEP_VTYSH_ARG_TIMER, + PCEP_VTYSH_ARG_SESSION_TIMEOUT, + group_opts->session_timeout_inteval_seconds); + lines++; + } + if (group_opts->tcp_md5_auth[0] != '\0') { + csnprintfrr(buf, buf_len, " %s %s\n", PCEP_VTYSH_ARG_TCP_MD5, + group_opts->tcp_md5_auth); + lines++; + } + if (group_opts->draft07) { + csnprintfrr(buf, buf_len, " %s\n", + PCEP_VTYSH_ARG_SR_DRAFT07); + lines++; + } + if (group_opts->pce_initiated) { + csnprintfrr(buf, buf_len, " %s\n", PCEP_VTYSH_ARG_PCE_INIT); + lines++; + } + + return lines; +} + +int pcep_cli_pce_config_write(struct vty *vty) +{ + int lines = 0; + char buf[1024] = ""; + + for (int i = 0; i < MAX_PCE; i++) { + struct pce_opts_cli *pce_opts_cli = pcep_g->pce_opts_cli[i]; + if (pce_opts_cli == NULL) { + continue; + } + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + + vty_out(vty, " pce %s\n", pce_opts->pce_name); + if (IS_IPADDR_V6(&pce_opts->addr)) { + vty_out(vty, " %s %s %pI6", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IPV6, &pce_opts->addr.ipaddr_v6); + } else if (IS_IPADDR_V4(&pce_opts->addr)) { + vty_out(vty, " address %s %pI4", PCEP_VTYSH_ARG_IP, + &pce_opts->addr.ipaddr_v4); + } + if (pce_opts->port != PCEP_DEFAULT_PORT) { + vty_out(vty, " %s %d", PCEP_VTYSH_ARG_PORT, + pce_opts->port); + } + vty_out(vty, "%s\n", buf); + lines += 2; + + if (pce_opts_cli->config_group_name[0] != '\0') { + vty_out(vty, " config %s\n", + pce_opts_cli->config_group_name); + lines++; + } + + /* Only display the values configured on the PCE, not the values + * from its optional pce-config-group, nor the default values */ + lines += pcep_cli_print_pce_config( + &pce_opts_cli->pce_config_group_opts, buf, sizeof(buf)); + + vty_out(vty, "%s", buf); + buf[0] = '\0'; + } + + return lines; +} + +int pcep_cli_pcep_pce_config_write(struct vty *vty) +{ + int lines = 0; + char buf[1024] = ""; + + for (int i = 0; i < MAX_PCE; i++) { + struct pcep_config_group_opts *group_opts = + pcep_g->config_group_opts[i]; + if (group_opts == NULL) { + continue; + } + + vty_out(vty, " pce-config %s\n", group_opts->name); + lines += 1; + + lines += + pcep_cli_print_pce_config(group_opts, buf, sizeof(buf)); + vty_out(vty, "%s", buf); + buf[0] = 0; + } + + return lines; +} + +/* + * VTYSH command syntax definitions + * The param names are taken from the path_pcep_cli_clippy.c generated file. + */ + +DEFPY(show_debugging_pathd_pcep, + show_debugging_pathd_pcep_cmd, + "show debugging pathd-pcep", + SHOW_STR + "State of each debugging option\n" + "pathd pcep module debugging\n") +{ + vty_out(vty, "Pathd pcep debugging status:\n"); + + if (DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_CONF)) { + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) + vty_out(vty, " Pathd pcep %s debugging is on\n", + PCEP_VTYSH_ARG_BASIC); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) + vty_out(vty, " Pathd pcep %s debugging is on\n", + PCEP_VTYSH_ARG_PATH); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) + vty_out(vty, " Pathd pcep %s debugging is on\n", + PCEP_VTYSH_ARG_MESSAGE); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB)) + vty_out(vty, " Pathd pcep %s debugging is on\n", + PCEP_VTYSH_ARG_PCEPLIB); + } + + return CMD_SUCCESS; +} + +DEFPY(pcep_cli_debug, + pcep_cli_debug_cmd, + "[no] debug pathd pcep [basic]$basic_str [path]$path_str [message]$message_str [pceplib]$pceplib_str", + NO_STR DEBUG_STR + "pathd debugging\n" + "pcep module debugging\n" + "module basic debugging\n" + "path structures debugging\n" + "pcep message debugging\n" + "pceplib debugging\n") +{ + return path_pcep_cli_debug(vty, no, basic_str, path_str, message_str, + pceplib_str); +} + +DEFPY(pcep_cli_show_srte_pcep_counters, + pcep_cli_show_srte_pcep_counters_cmd, + "show sr-te pcep counters", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "PCEP counters\n") +{ + return path_pcep_cli_show_srte_pcep_counters(vty); +} + +DEFPY_NOSH( + pcep_cli_pcep, + pcep_cli_pcep_cmd, + "pcep", + "PCEP configuration\n") +{ + vty->node = PCEP_NODE; + return CMD_SUCCESS; +} + +DEFPY_NOSH( + pcep_cli_pcep_pce_config, + pcep_cli_pcep_pce_config_cmd, + "[no] pce-config WORD$name", + NO_STR + "Shared configuration\n" + "Shared configuration name\n") +{ + if (no == NULL) + return path_pcep_cli_pcep_pce_config(vty, name); + return path_pcep_cli_pcep_pce_config_delete(vty, name); +} + +DEFPY(pcep_cli_show_srte_pcep_pce_config, + pcep_cli_show_srte_pcep_pce_config_cmd, + "show sr-te pcep pce-config [<default|WORD>$name]", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "Show shared PCE configuration\n" + "Show default hard-coded values\n" + "Shared configuration name\n") +{ + return path_pcep_cli_show_srte_pcep_pce_config(vty, name); +} + +DEFPY_NOSH( + pcep_cli_pce, + pcep_cli_pce_cmd, + "[no] pce WORD$name", + NO_STR + "PCE configuration, address sub-config is mandatory\n" + "PCE name\n") +{ + if (no == NULL) + return path_pcep_cli_pce(vty, name); + return path_pcep_cli_pce_delete(vty, name); +} + +DEFPY(pcep_cli_show_srte_pcep_pce, + pcep_cli_show_srte_pcep_pce_cmd, + "show sr-te pcep pce [WORD$name]", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "Show detailed pce values\n" + "pce name\n") +{ + return path_pcep_cli_show_srte_pcep_pce(vty, name); +} + +DEFPY(pcep_cli_peer_sr_draft07, + pcep_cli_peer_sr_draft07_cmd, + "sr-draft07", + "Configure PCC to send PCEP Open with SR draft07\n") +{ + return path_pcep_cli_peer_sr_draft07(vty); +} + +DEFPY(pcep_cli_peer_pce_initiated, + pcep_cli_peer_pce_initiated_cmd, + "pce-initiated", + "Configure PCC to accept PCE initiated LSPs\n") +{ + return path_pcep_cli_peer_pce_initiated(vty); +} + +DEFPY(pcep_cli_peer_tcp_md5_auth, + pcep_cli_peer_tcp_md5_auth_cmd, + "tcp-md5-auth WORD", + "Configure PCC TCP-MD5 RFC2385 Authentication\n" + "TCP-MD5 Authentication string\n") +{ + return path_pcep_cli_peer_tcp_md5_auth(vty, tcp_md5_auth); +} + +DEFPY(pcep_cli_peer_address, + pcep_cli_peer_address_cmd, + "address <ip A.B.C.D | ipv6 X:X::X:X> [port (1024-65535)]", + "PCE IP Address configuration, mandatory configuration\n" + "PCE IPv4 address\n" + "Remote PCE server IPv4 address\n" + "PCE IPv6 address\n" + "Remote PCE server IPv6 address\n" + "Remote PCE server port\n" + "Remote PCE server port value\n") +{ + return path_pcep_cli_peer_address(vty, ip_str, &ip, ipv6_str, &ipv6, + port_str, port); +} + +DEFPY(pcep_cli_peer_source_address, + pcep_cli_peer_source_address_cmd, + "source-address [ip A.B.C.D | ipv6 X:X::X:X] [port (1024-65535)]", + "PCE source IP Address configuration\n" + "PCE source IPv4 address\n" + "PCE source IPv4 address value\n" + "PCE source IPv6 address\n" + "PCE source IPv6 address value\n" + "Source PCE server port\n" + "Source PCE server port value\n") +{ + return path_pcep_cli_peer_source_address(vty, ip_str, &ip, ipv6_str, + &ipv6, port_str, port); +} + +DEFPY(pcep_cli_peer_pcep_pce_config_ref, + pcep_cli_peer_pcep_pce_config_ref_cmd, + "config WORD$name", + "PCE shared configuration to use\n" + "Shared configuration name\n") +{ + return path_pcep_cli_peer_pcep_pce_config_ref(vty, name); +} + +DEFPY(pcep_cli_peer_timers, + pcep_cli_peer_timers_cmd, + "timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] " + "[dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] " + "[pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)]", + "PCE PCEP Session Timers configuration\n" + "PCC Keep Alive Timer\n" + "PCC Keep Alive Timer value in seconds\n" + "Min Acceptable PCE Keep Alive Timer\n" + "Min Acceptable PCE Keep Alive Timer value in seconds\n" + "Max Acceptable PCE Keep Alive Timer\n" + "Max Acceptable PCE Keep Alive Timer value in seconds\n" + "PCC Dead Timer\n" + "PCC Dead Timer value in seconds\n" + "Min Acceptable PCE Dead Timer\n" + "Min Acceptable PCE Dead Timer value in seconds\n" + "Max Acceptable PCE Dead Timer\n" + "Max Acceptable PCE Dead Timer value in seconds\n" + "PCC PCEP Request Timer\n" + "PCC PCEP Request Timer value in seconds\n" + "PCC Session Timeout Interval\n" + "PCC Session Timeout Interval value in seconds\n" + "Multi-PCE delegation timeout\n" + "Multi-PCE delegation timeout value in seconds\n") +{ + return path_pcep_cli_peer_timers( + vty, keep_alive_str, keep_alive, min_peer_keep_alive_str, + min_peer_keep_alive, max_peer_keep_alive_str, + max_peer_keep_alive, dead_timer_str, dead_timer, + min_peer_dead_timer_str, min_peer_dead_timer, + max_peer_dead_timer_str, max_peer_dead_timer, pcep_request_str, + pcep_request, session_timeout_interval_str, + session_timeout_interval, delegation_timeout_str, + delegation_timeout); +} + +DEFPY_NOSH( + pcep_cli_pcc, + pcep_cli_pcc_cmd, + "[no] pcc", + NO_STR + "PCC configuration\n") +{ + if (no != NULL) { + return path_pcep_cli_pcc_delete(vty); + } else { + return path_pcep_cli_pcc(vty); + } +} + +DEFPY(pcep_cli_pcc_pcc_msd, + pcep_cli_pcc_pcc_msd_cmd, + "msd (1-32)", + "PCC maximum SID depth \n" + "PCC maximum SID depth value\n") +{ + return path_pcep_cli_pcc_pcc_msd(vty, msd_str, msd); +} + +DEFPY(pcep_cli_pcc_pcc_peer, + pcep_cli_pcc_pcc_peer_cmd, + "[no] peer WORD [precedence (1-255)]", + NO_STR + "PCC PCE peer\n" + "PCC PCE name\n" + "PCC Multi-PCE precedence\n" + "PCE precedence\n") +{ + if (no != NULL) { + return path_pcep_cli_pcc_pcc_peer_delete( + vty, peer, precedence_str, precedence); + } else { + return path_pcep_cli_pcc_pcc_peer(vty, peer, precedence_str, + precedence); + } +} + +DEFPY(pcep_cli_show_srte_pcc, + pcep_cli_show_srte_pcc_cmd, + "show sr-te pcep pcc", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "Show current PCC configuration\n") +{ + return path_pcep_cli_show_srte_pcep_pcc(vty); +} + +DEFPY(pcep_cli_show_srte_pcep_session, + pcep_cli_show_srte_pcep_session_cmd, + "show sr-te pcep session [WORD]$pce", + SHOW_STR + "SR-TE info\n" + "PCEP info\n" + "Show PCEP Session information\n" + "PCE name\n") +{ + return path_pcep_cli_show_srte_pcep_session(vty, pce); +} + +DEFPY(pcep_cli_clear_srte_pcep_session, + pcep_cli_clear_srte_pcep_session_cmd, + "clear sr-te pcep session [WORD]$pce", + CLEAR_STR + "SR-TE\n" + "PCEP\n" + "Reset PCEP connection\n" + "PCE name\n") +{ + return path_pcep_cli_clear_srte_pcep_session(vty, pce); +} + +void pcep_cli_init(void) +{ + hook_register(nb_client_debug_config_write, + pcep_cli_debug_config_write); + hook_register(nb_client_debug_set_all, pcep_cli_debug_set_all); + + memset(&pce_connections_g, 0, sizeof(pce_connections_g)); + + install_node(&pcep_node); + install_node(&pcep_pcc_node); + install_node(&pcep_pce_node); + install_node(&pcep_pce_config_node); + + install_default(PCEP_PCE_CONFIG_NODE); + install_default(PCEP_PCE_NODE); + install_default(PCEP_PCC_NODE); + install_default(PCEP_NODE); + + install_element(SR_TRAFFIC_ENG_NODE, &pcep_cli_pcep_cmd); + + /* PCEP configuration group related configuration commands */ + install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd); + install_element(PCEP_PCE_CONFIG_NODE, + &pcep_cli_peer_source_address_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_timers_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_sr_draft07_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_pce_initiated_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_tcp_md5_auth_cmd); + + /* PCE peer related configuration commands */ + install_element(PCEP_NODE, &pcep_cli_pce_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_address_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_source_address_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_pcep_pce_config_ref_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_timers_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_sr_draft07_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_pce_initiated_cmd); + install_element(PCEP_PCE_NODE, &pcep_cli_peer_tcp_md5_auth_cmd); + + /* PCC related configuration commands */ + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcc_cmd); + install_element(PCEP_NODE, &pcep_cli_pcc_cmd); + install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_peer_cmd); + install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_msd_cmd); + + /* Top commands */ + install_element(CONFIG_NODE, &pcep_cli_debug_cmd); + install_element(ENABLE_NODE, &pcep_cli_debug_cmd); + install_element(ENABLE_NODE, &show_debugging_pathd_pcep_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_counters_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_config_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_session_cmd); + install_element(ENABLE_NODE, &pcep_cli_clear_srte_pcep_session_cmd); +} diff --git a/pathd/path_pcep_cli.h b/pathd/path_pcep_cli.h new file mode 100644 index 000000000..0b101ab21 --- /dev/null +++ b/pathd/path_pcep_cli.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * Brady Johnson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_CLI_H_ +#define _PATH_PCEP_CLI_H_ + + +/* PCEP CLI Functions */ +void pcep_cli_init(void); + +#endif // _PATH_PCEP_CLI_H_ diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c new file mode 100644 index 000000000..989223ebc --- /dev/null +++ b/pathd/path_pcep_config.c @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <northbound.h> +#include <yang.h> +#include <printfrr.h> +#include <pcep-objects.h> +#include "pathd/pathd.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_config.h" +#include "pathd/path_pcep_debug.h" +#include "thread.h" + +#define MAX_XPATH 256 +#define MAX_FLOAT_LEN 22 +#define INETADDR4_MAXLEN 16 +#define INETADDR6_MAXLEN 40 + + +static void copy_candidate_objfun_info(struct srte_candidate *candidate, + struct path *path); +static void copy_candidate_affinity_filters(struct srte_candidate *candidate, + struct path *path); +static struct path_hop * +path_pcep_config_list_path_hops(struct srte_segment_list *segment_list); +static struct srte_candidate *lookup_candidate(struct lsp_nb_key *key); +static char *candidate_name(struct srte_candidate *candidate); +static enum pcep_lsp_operational_status +status_int_to_ext(enum srte_policy_status status); +static enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type); +static enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type); + +static int path_pcep_config_lookup_cb(struct thread *t) +{ + struct path *path = THREAD_ARG(t); + struct srte_candidate *candidate = lookup_candidate(&path->nbkey); + struct srte_lsp *lsp; + + if (candidate == NULL) + return 0; + + lsp = candidate->lsp; + + if (path->name == NULL) + path->name = candidate_name(candidate); + if (path->type == SRTE_CANDIDATE_TYPE_UNDEFINED) + path->type = candidate->type; + if (path->create_origin == SRTE_ORIGIN_UNDEFINED) + path->create_origin = candidate->protocol_origin; + if ((path->update_origin == SRTE_ORIGIN_UNDEFINED) + && (lsp->segment_list != NULL)) + path->update_origin = lsp->segment_list->protocol_origin; + + return 0; +} + +void path_pcep_config_lookup(struct path *path) +{ + /* + * Configuration access is strictly done via the main thread + */ + thread_execute(master, path_pcep_config_lookup_cb, path, 0); +} + +struct path *path_pcep_config_get_path(struct lsp_nb_key *key) +{ + struct srte_candidate *candidate = lookup_candidate(key); + if (candidate == NULL) + return NULL; + return candidate_to_path(candidate); +} + +void path_pcep_config_list_path(path_list_cb_t cb, void *arg) +{ + struct path *path; + struct srte_policy *policy; + struct srte_candidate *candidate; + + RB_FOREACH (policy, srte_policy_head, &srte_policies) { + RB_FOREACH (candidate, srte_candidate_head, + &policy->candidate_paths) { + path = candidate_to_path(candidate); + if (!cb(path, arg)) + return; + } + } +} + +struct path *candidate_to_path(struct srte_candidate *candidate) +{ + char *name; + struct path *path; + struct path_hop *hop = NULL; + struct path_metric *metric = NULL; + struct srte_policy *policy; + struct srte_lsp *lsp; + enum pcep_lsp_operational_status status; + enum srte_protocol_origin update_origin = 0; + char *originator = NULL; + + policy = candidate->policy; + lsp = candidate->lsp; + + if (lsp->segment_list != NULL) { + hop = path_pcep_config_list_path_hops(lsp->segment_list); + update_origin = lsp->segment_list->protocol_origin; + originator = XSTRDUP(MTYPE_PCEP, lsp->segment_list->originator); + } + path = pcep_new_path(); + name = candidate_name(candidate); + if (CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)) { + status = status_int_to_ext(policy->status); + } else { + status = PCEP_LSP_OPERATIONAL_DOWN; + } + for (uint32_t i = 0; i < MAX_METRIC_TYPE; i++) { + struct path_metric *path_metric; + struct srte_metric *srte_metric = &lsp->metrics[i]; + if (CHECK_FLAG(srte_metric->flags, F_METRIC_IS_DEFINED)) { + path_metric = pcep_new_metric(); + path_metric->next = metric; + metric = path_metric; + metric->type = i + 1; + metric->value = srte_metric->value; + metric->enforce = CHECK_FLAG(srte_metric->flags, + F_METRIC_IS_REQUIRED); + metric->is_bound = CHECK_FLAG(srte_metric->flags, + F_METRIC_IS_BOUND); + metric->is_computed = CHECK_FLAG(srte_metric->flags, + F_METRIC_IS_COMPUTED); + } + } + *path = (struct path){ + .nbkey = (struct lsp_nb_key){.color = policy->color, + .endpoint = policy->endpoint, + .preference = + candidate->preference}, + .create_origin = lsp->protocol_origin, + .update_origin = update_origin, + .originator = originator, + .plsp_id = 0, + .name = name, + .type = candidate->type, + .srp_id = 0, + .req_id = 0, + .binding_sid = policy->binding_sid, + .status = status, + .do_remove = false, + .go_active = false, + .was_created = false, + .was_removed = false, + .is_synching = false, + .is_delegated = false, + .first_hop = hop, + .first_metric = metric}; + + path->has_bandwidth = CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); + if (path->has_bandwidth) { + path->enforce_bandwidth = + CHECK_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); + path->bandwidth = lsp->bandwidth; + } else { + path->enforce_bandwidth = true; + path->bandwidth = 0; + } + + copy_candidate_objfun_info(candidate, path); + copy_candidate_affinity_filters(candidate, path); + + return path; +} + +void copy_candidate_objfun_info(struct srte_candidate *candidate, + struct path *path) +{ + struct srte_lsp *lsp = candidate->lsp; + + if (lsp != NULL) { + if (CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_OBJFUN)) { + path->has_pce_objfun = true; + path->pce_objfun = lsp->objfun; + } else { + path->has_pce_objfun = false; + path->pce_objfun = OBJFUN_UNDEFINED; + } + } + if (CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN)) { + path->has_pcc_objfun = true; + path->pcc_objfun = candidate->objfun; + path->enforce_pcc_objfun = CHECK_FLAG( + candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN); + + } else { + path->has_pcc_objfun = false; + path->pcc_objfun = OBJFUN_UNDEFINED; + UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN); + } +} + +void copy_candidate_affinity_filters(struct srte_candidate *candidate, + struct path *path) +{ + bool eany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_EXCLUDE_ANY); + bool iany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ANY); + bool iall = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ALL); + path->has_affinity_filters = eany || iany || iall; + path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1] = + eany ? candidate->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY + - 1] + : 0; + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1] = + iany ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY + - 1] + : 0; + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1] = + iall ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL + - 1] + : 0; +} + +struct path_hop * +path_pcep_config_list_path_hops(struct srte_segment_list *segment_list) +{ + struct srte_segment_entry *segment; + struct path_hop *hop = NULL, *last_hop = NULL; + + RB_FOREACH_REVERSE (segment, srte_segment_entry_head, + &segment_list->segments) { + hop = pcep_new_hop(); + *hop = (struct path_hop){ + .next = last_hop, + .is_loose = false, + .has_sid = true, + .is_mpls = true, + .has_attribs = false, + .sid = {.mpls = {.label = segment->sid_value}}, + .has_nai = + segment->nai_type != SRTE_SEGMENT_NAI_TYPE_NONE, + .nai = {.type = pcep_nai_type(segment->nai_type)}}; + switch (segment->nai_type) { + case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: + case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: + memcpy(&hop->nai.local_addr, &segment->nai_local_addr, + sizeof(struct ipaddr)); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + memcpy(&hop->nai.local_addr, &segment->nai_local_addr, + sizeof(struct ipaddr)); + memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr, + sizeof(struct ipaddr)); + break; + case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: + memcpy(&hop->nai.local_addr, &segment->nai_local_addr, + sizeof(struct ipaddr)); + hop->nai.local_iface = segment->nai_local_iface; + memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr, + sizeof(struct ipaddr)); + hop->nai.remote_iface = segment->nai_remote_iface; + break; + default: + break; + } + last_hop = hop; + } + return hop; +} + +int path_pcep_config_update_path(struct path *path) +{ + assert(path != NULL); + assert(path->nbkey.preference != 0); + assert(path->nbkey.endpoint.ipa_type == IPADDR_V4); + + struct path_hop *hop; + struct path_metric *metric; + int index; + char segment_list_name_buff[64 + 1 + 64 + 1 + 11 + 1]; + char *segment_list_name = NULL; + struct srte_candidate *candidate; + struct srte_segment_list *segment_list = NULL; + struct srte_segment_entry *segment; + + candidate = lookup_candidate(&path->nbkey); + + // if there is no candidate to update we are done + if (!candidate) + return 0; + + // first clean up old segment list if present + if (candidate->lsp->segment_list) { + SET_FLAG(candidate->lsp->segment_list->flags, + F_SEGMENT_LIST_DELETED); + candidate->lsp->segment_list = NULL; + } + + if (path->first_hop != NULL) { + snprintf(segment_list_name_buff, sizeof(segment_list_name_buff), + "%s-%u", path->name, path->plsp_id); + segment_list_name = segment_list_name_buff; + + segment_list = srte_segment_list_add(segment_list_name); + segment_list->protocol_origin = path->update_origin; + strlcpy(segment_list->originator, path->originator, + sizeof(segment_list->originator)); + SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); + SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + + for (hop = path->first_hop, index = 10; hop != NULL; + hop = hop->next, index += 10) { + assert(hop->has_sid); + assert(hop->is_mpls); + + segment = srte_segment_entry_add(segment_list, index); + + segment->sid_value = (mpls_label_t)hop->sid.mpls.label; + SET_FLAG(segment->segment_list->flags, + F_SEGMENT_LIST_MODIFIED); + + if (hop->has_nai) + srte_segment_entry_set_nai( + segment, srte_nai_type(hop->nai.type), + &hop->nai.local_addr, + hop->nai.local_iface, + &hop->nai.remote_addr, + hop->nai.remote_iface); + } + } + + candidate->lsp->segment_list = segment_list; + SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + + for (metric = path->first_metric; metric != NULL; metric = metric->next) + srte_lsp_set_metric(candidate->lsp, metric->type, metric->value, + metric->enforce, metric->is_bound, + metric->is_computed); + + if (path->has_bandwidth) + srte_lsp_set_bandwidth(candidate->lsp, path->bandwidth, + path->enforce_bandwidth); + + if (path->has_pce_objfun) { + SET_FLAG(candidate->lsp->flags, F_CANDIDATE_HAS_OBJFUN); + candidate->lsp->objfun = path->pce_objfun; + } + + srte_apply_changes(); + + return 0; +} + +struct srte_candidate *lookup_candidate(struct lsp_nb_key *key) +{ + struct srte_policy *policy = NULL; + policy = srte_policy_find(key->color, &key->endpoint); + if (policy == NULL) + return NULL; + return srte_candidate_find(policy, key->preference); +} + +char *candidate_name(struct srte_candidate *candidate) +{ + return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name, + candidate->name); +} + +enum pcep_lsp_operational_status +status_int_to_ext(enum srte_policy_status status) +{ + switch (status) { + case SRTE_POLICY_STATUS_UP: + return PCEP_LSP_OPERATIONAL_ACTIVE; + case SRTE_POLICY_STATUS_GOING_UP: + return PCEP_LSP_OPERATIONAL_GOING_UP; + case SRTE_POLICY_STATUS_GOING_DOWN: + return PCEP_LSP_OPERATIONAL_GOING_DOWN; + default: + return PCEP_LSP_OPERATIONAL_DOWN; + } +} + +enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type) +{ + switch (type) { + case SRTE_SEGMENT_NAI_TYPE_NONE: + return PCEP_SR_SUBOBJ_NAI_ABSENT; + case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: + return PCEP_SR_SUBOBJ_NAI_IPV4_NODE; + case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: + return PCEP_SR_SUBOBJ_NAI_IPV6_NODE; + case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: + return PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY; + case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: + return PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY; + case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: + return PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY; + default: + return PCEP_SR_SUBOBJ_NAI_UNKNOWN; + } +} + +enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type) +{ + switch (type) { + case PCEP_SR_SUBOBJ_NAI_ABSENT: + return SRTE_SEGMENT_NAI_TYPE_NONE; + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + return SRTE_SEGMENT_NAI_TYPE_IPV4_NODE; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + return SRTE_SEGMENT_NAI_TYPE_IPV6_NODE; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + return SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + return SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + return SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY; + default: + return SRTE_SEGMENT_NAI_TYPE_NONE; + } +} diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h new file mode 100644 index 000000000..de29ab29c --- /dev/null +++ b/pathd/path_pcep_config.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_CONFIG_H_ +#define _PATH_PCEP_CONFIG_H_ + +#include <stdbool.h> +#include <debug.h> + +#include "pathd/path_pcep.h" + +#define PATH_NB_NO_CHANGE 0 +#define PATH_NB_OK 1 +#define PATH_NB_ERR -1 + +typedef int (*path_list_cb_t)(struct path *path, void *arg); + +/* Lookup the candidate path and fill up the missing path attributes like name + and type. Used for path generated from PCEP message received from the PCE + so they contains more information about the candidate path. If no matching + policy or candidate path is found, nothing is changed */ +void path_pcep_config_lookup(struct path *path); +struct path *path_pcep_config_get_path(struct lsp_nb_key *key); +void path_pcep_config_list_path(path_list_cb_t cb, void *arg); +int path_pcep_config_update_path(struct path *path); +struct path *candidate_to_path(struct srte_candidate *candidate); + + +#endif // _PATH_PCEP_CONFIG_H_ diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c new file mode 100644 index 000000000..e467a79a8 --- /dev/null +++ b/pathd/path_pcep_controller.c @@ -0,0 +1,1077 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" + +#include "pathd/pathd.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_pcc.h" +#include "pathd/path_pcep_config.h" +#include "pathd/path_pcep_debug.h" + +#define MAX_RECONNECT_DELAY 120 + +#define min(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a <= _b ? _a : _b; \ + }) + + +/* Event handling data structures */ +enum pcep_ctrl_event_type { + EV_UPDATE_PCC_OPTS = 1, + EV_UPDATE_PCE_OPTS, + EV_REMOVE_PCC, + EV_PATHD_EVENT, + EV_SYNC_PATH, + EV_SYNC_DONE, + EV_PCEPLIB_EVENT, + EV_RESET_PCC_SESSION +}; + +struct pcep_ctrl_event_data { + struct ctrl_state *ctrl_state; + enum pcep_ctrl_event_type type; + uint32_t sub_type; + int pcc_id; + void *payload; +}; + +struct pcep_main_event_data { + pcep_main_event_handler_t handler; + int pcc_id; + enum pcep_main_event_type type; + void *payload; +}; + +/* Synchronous call arguments */ + +struct get_counters_args { + struct ctrl_state *ctrl_state; + int pcc_id; + struct counters_group *counters; +}; + +struct send_report_args { + struct ctrl_state *ctrl_state; + int pcc_id; + struct path *path; +}; + +struct get_pcep_session_args { + struct ctrl_state *ctrl_state; + int pcc_id; + pcep_session *pcep_session; +}; + +/* Internal Functions Called From Main Thread */ +static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res); + +/* Internal Functions Called From Controller Thread */ +static int pcep_thread_finish_event_handler(struct thread *thread); +static int pcep_thread_get_counters_callback(struct thread *t); +static int pcep_thread_send_report_callback(struct thread *t); +static int pcep_thread_get_pcep_session_callback(struct thread *t); +static int pcep_thread_get_pcc_info_callback(struct thread *t); + +/* Controller Thread Timer Handler */ +static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timer_type timer_type, + enum pcep_ctrl_timeout_type timeout_type, + uint32_t delay, void *payload, + struct thread **thread); +static int schedule_thread_timer_with_cb( + struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timer_type timer_type, + enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload, + struct thread **thread, pcep_ctrl_thread_callback timer_cb); +static int pcep_thread_timer_handler(struct thread *thread); + +/* Controller Thread Socket read/write Handler */ +static int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_socket_type type, bool is_read, + void *payload, int fd, struct thread **thread, + pcep_ctrl_thread_callback cb); + +/* Controller Thread Event Handler */ +static int send_to_thread(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_event_type type, uint32_t sub_type, + void *payload); +static int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_event_type type, + uint32_t sub_type, void *payload, + pcep_ctrl_thread_callback event_cb); +static int pcep_thread_event_handler(struct thread *thread); +static int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state, + struct pcc_opts *opts); +static int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state, + int pcc_id, + struct pce_opts *opts); +static int pcep_thread_event_remove_pcc_by_id(struct ctrl_state *ctrl_state, + int pcc_id); +static int pcep_thread_event_remove_pcc_all(struct ctrl_state *ctrl_state); +static int pcep_thread_event_remove_pcc(struct ctrl_state *ctrl_state, + struct pce_opts *pce_opts); +static int pcep_thread_event_sync_path(struct ctrl_state *ctrl_state, + int pcc_id, struct path *path); +static int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state, + int pcc_id); +static int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state, + enum pcep_pathd_event_type type, + struct path *path); + +/* Main Thread Event Handler */ +static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_main_event_type type, void *payload); +static int pcep_main_event_handler(struct thread *thread); + +/* Helper functions */ +static void set_ctrl_state(struct frr_pthread *fpt, + struct ctrl_state *ctrl_state); +static struct ctrl_state *get_ctrl_state(struct frr_pthread *fpt); +int get_next_id(struct ctrl_state *ctrl_state); +int set_pcc_state(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state); +void remove_pcc_state(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +static uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t attempt); +static const char *timer_type_name(enum pcep_ctrl_timer_type type); +static const char *timeout_type_name(enum pcep_ctrl_timeout_type type); + + +/* ------------ API Functions Called from Main Thread ------------ */ + +int pcep_ctrl_initialize(struct thread_master *main_thread, + struct frr_pthread **fpt, + pcep_main_event_handler_t event_handler) +{ + assert(fpt != NULL); + + int ret = 0; + struct ctrl_state *ctrl_state; + struct frr_pthread_attr attr = { + .start = frr_pthread_attr_default.start, + .stop = pcep_ctrl_halt_cb, + }; + + PCEP_DEBUG("Initializing pcep module controller"); + + /* Create and start the FRR pthread */ + *fpt = frr_pthread_new(&attr, "PCEP thread", "pcep"); + if (*fpt == NULL) { + flog_err(EC_PATH_SYSTEM_CALL, + "failed to initialize PCEP thread"); + return 1; + } + ret = frr_pthread_run(*fpt, NULL); + if (ret < 0) { + flog_err(EC_PATH_SYSTEM_CALL, "failed to create PCEP thread"); + return ret; + } + frr_pthread_wait_running(*fpt); + + /* Initialize the thread state */ + ctrl_state = XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state)); + ctrl_state->main = main_thread; + ctrl_state->self = (*fpt)->master; + ctrl_state->main_event_handler = event_handler; + ctrl_state->pcc_count = 0; + ctrl_state->pcc_last_id = 0; + ctrl_state->pcc_opts = + XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state->pcc_opts)); + /* Default to no PCC address defined */ + ctrl_state->pcc_opts->addr.ipa_type = IPADDR_NONE; + ctrl_state->pcc_opts->port = PCEP_DEFAULT_PORT; + + /* Keep the state reference for events */ + set_ctrl_state(*fpt, ctrl_state); + + return ret; +} + +int pcep_ctrl_finalize(struct frr_pthread **fpt) +{ + assert(fpt != NULL); + + int ret = 0; + + PCEP_DEBUG("Finalizing pcep module controller"); + + if (*fpt != NULL) { + frr_pthread_stop(*fpt, NULL); + *fpt = NULL; + } + + return ret; +} + +int pcep_ctrl_update_pcc_options(struct frr_pthread *fpt, struct pcc_opts *opts) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, 0, EV_UPDATE_PCC_OPTS, 0, opts); +} + +int pcep_ctrl_update_pce_options(struct frr_pthread *fpt, struct pce_opts *opts) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, 0, EV_UPDATE_PCE_OPTS, 0, opts); +} + +int pcep_ctrl_remove_pcc(struct frr_pthread *fpt, struct pce_opts *pce_opts) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, 0, EV_REMOVE_PCC, 0, pce_opts); +} + +int pcep_ctrl_reset_pcc_session(struct frr_pthread *fpt, char *pce_name) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, 0, EV_RESET_PCC_SESSION, 0, pce_name); +} + +int pcep_ctrl_pathd_event(struct frr_pthread *fpt, + enum pcep_pathd_event_type type, struct path *path) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, 0, EV_PATHD_EVENT, type, path); +} + +int pcep_ctrl_sync_path(struct frr_pthread *fpt, int pcc_id, struct path *path) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, pcc_id, EV_SYNC_PATH, 0, path); +} + +int pcep_ctrl_sync_done(struct frr_pthread *fpt, int pcc_id) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return send_to_thread(ctrl_state, pcc_id, EV_SYNC_DONE, 0, NULL); +} + +struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt, + int pcc_id) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + struct get_counters_args args = { + .ctrl_state = ctrl_state, .pcc_id = pcc_id, .counters = NULL}; + thread_execute(ctrl_state->self, pcep_thread_get_counters_callback, + &args, 0); + return args.counters; +} + +pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + struct get_pcep_session_args args = {.ctrl_state = ctrl_state, + .pcc_id = pcc_id, + .pcep_session = NULL}; + thread_execute(ctrl_state->self, pcep_thread_get_pcep_session_callback, + &args, 0); + return args.pcep_session; +} + +struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt, + const char *pce_name) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + struct pcep_pcc_info *args = XCALLOC(MTYPE_PCEP, sizeof(*args)); + args->ctrl_state = ctrl_state; + strncpy(args->pce_name, pce_name, sizeof(args->pce_name)); + thread_execute(ctrl_state->self, pcep_thread_get_pcc_info_callback, + args, 0); + + return args; +} + +void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, + struct path *path) +{ + /* Sends a report stynchronously */ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + struct send_report_args args = { + .ctrl_state = ctrl_state, .pcc_id = pcc_id, .path = path}; + thread_execute(ctrl_state->self, pcep_thread_send_report_callback, + &args, 0); +} + +/* ------------ Internal Functions Called from Main Thread ------------ */ + +int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res) +{ + thread_add_event(fpt->master, pcep_thread_finish_event_handler, + (void *)fpt, 0, NULL); + pthread_join(fpt->thread, res); + + return 0; +} + + +/* ------------ API Functions Called From Controller Thread ------------ */ + +void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id) +{ + send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_START_SYNC, NULL); +} + +void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id, + struct path *path) +{ + send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_UPDATE_CANDIDATE, + path); +} + +void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + if (!pcc_state) + return; + /* Will be deleted when the event is handled */ + char *originator = XSTRDUP(MTYPE_PCEP, pcc_state->originator); + PCEP_DEBUG("schedule candidate path segments removal for originator %s", + originator); + send_to_main(ctrl_state, pcep_pcc_get_pcc_id(pcc_state), + PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP, originator); +} + +void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state, + int pcc_id, int delay, + struct thread **thread) +{ + + schedule_thread_timer(ctrl_state, pcc_id, TM_CALCULATE_BEST_PCE, + TO_UNDEFINED, delay, NULL, thread); +} + +void pcep_thread_cancel_timer(struct thread **thread) +{ + if (thread == NULL || *thread == NULL) { + return; + } + + struct pcep_ctrl_timer_data *data = THREAD_ARG(*thread); + PCEP_DEBUG("Timer %s / %s canceled", timer_type_name(data->timer_type), + timeout_type_name(data->timeout_type)); + if (data != NULL) { + XFREE(MTYPE_PCEP, data); + } + + if ((*thread)->master->owner == pthread_self()) { + thread_cancel(thread); + } else { + thread_cancel_async((*thread)->master, thread, NULL); + } +} + +void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, + int retry_count, struct thread **thread) +{ + uint32_t delay = backoff_delay(MAX_RECONNECT_DELAY, 1, retry_count); + PCEP_DEBUG("Schedule RECONNECT_PCC for %us (retry %u)", delay, + retry_count); + schedule_thread_timer(ctrl_state, pcc_id, TM_RECONNECT_PCC, + TO_UNDEFINED, delay, NULL, thread); +} + +void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timeout_type timeout_type, + uint32_t delay, void *param, + struct thread **thread) +{ + assert(timeout_type > TO_UNDEFINED); + assert(timeout_type < TO_MAX); + PCEP_DEBUG("Schedule timeout %s for %us", + timeout_type_name(timeout_type), delay); + schedule_thread_timer(ctrl_state, pcc_id, TM_TIMEOUT, timeout_type, + delay, param, thread); +} + +void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state, + int delay, void *payload, + struct thread **thread, + pcep_ctrl_thread_callback timer_cb) +{ + PCEP_DEBUG("Schedule PCEPLIB_TIMER for %us", delay); + schedule_thread_timer_with_cb(ctrl_state, 0, TM_PCEPLIB_TIMER, + TO_UNDEFINED, delay, payload, thread, + timer_cb); +} + +void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state, + int pcc_id, int delay, + struct thread **thread) +{ + PCEP_DEBUG("Schedule session_timeout interval for %us", delay); + schedule_thread_timer(ctrl_state, pcc_id, TM_SESSION_TIMEOUT_PCC, + TO_UNDEFINED, delay, NULL, thread); +} + +int pcep_thread_pcc_count(struct ctrl_state *ctrl_state) +{ + if (ctrl_state == NULL) { + return 0; + } + + return ctrl_state->pcc_count; +} + +/* ------------ Internal Functions Called From Controller Thread ------------ */ + +int pcep_thread_finish_event_handler(struct thread *thread) +{ + int i; + struct frr_pthread *fpt = THREAD_ARG(thread); + struct ctrl_state *ctrl_state = fpt->data; + + assert(ctrl_state != NULL); + + for (i = 0; i < MAX_PCC; i++) { + if (ctrl_state->pcc[i]) { + pcep_pcc_finalize(ctrl_state, ctrl_state->pcc[i]); + ctrl_state->pcc[i] = NULL; + } + } + + XFREE(MTYPE_PCEP, ctrl_state->pcc_opts); + XFREE(MTYPE_PCEP, ctrl_state); + fpt->data = NULL; + + atomic_store_explicit(&fpt->running, false, memory_order_relaxed); + return 0; +} + +int pcep_thread_get_counters_callback(struct thread *t) +{ + struct get_counters_args *args = THREAD_ARG(t); + assert(args != NULL); + struct ctrl_state *ctrl_state = args->ctrl_state; + assert(ctrl_state != NULL); + struct pcc_state *pcc_state; + + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id); + if (pcc_state) { + args->counters = pcep_lib_copy_counters(pcc_state->sess); + } else { + args->counters = NULL; + } + + return 0; +} + +int pcep_thread_send_report_callback(struct thread *t) +{ + struct send_report_args *args = THREAD_ARG(t); + assert(args != NULL); + struct ctrl_state *ctrl_state = args->ctrl_state; + assert(ctrl_state != NULL); + struct pcc_state *pcc_state; + + if (args->pcc_id == 0) { + for (int i = 0; i < MAX_PCC; i++) { + if (ctrl_state->pcc[i]) { + pcep_pcc_send_report(ctrl_state, + ctrl_state->pcc[i], + args->path); + } + } + } else { + pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id); + pcep_pcc_send_report(ctrl_state, pcc_state, args->path); + } + + return 0; +} + +int pcep_thread_get_pcep_session_callback(struct thread *t) +{ + struct get_pcep_session_args *args = THREAD_ARG(t); + assert(args != NULL); + struct ctrl_state *ctrl_state = args->ctrl_state; + assert(ctrl_state != NULL); + struct pcc_state *pcc_state; + + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id); + if (pcc_state) { + args->pcep_session = + pcep_lib_copy_pcep_session(pcc_state->sess); + } + + return 0; +} + +int pcep_thread_get_pcc_info_callback(struct thread *t) +{ + struct pcep_pcc_info *args = THREAD_ARG(t); + assert(args != NULL); + struct ctrl_state *ctrl_state = args->ctrl_state; + assert(ctrl_state != NULL); + + pcep_pcc_copy_pcc_info(ctrl_state->pcc, args); + + return 0; +} + +/* ------------ Controller Thread Timer Handler ------------ */ + +int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timer_type timer_type, + enum pcep_ctrl_timeout_type timeout_type, + uint32_t delay, void *payload, + struct thread **thread, + pcep_ctrl_thread_callback timer_cb) +{ + assert(thread != NULL); + + struct pcep_ctrl_timer_data *data; + + data = XCALLOC(MTYPE_PCEP, sizeof(*data)); + data->ctrl_state = ctrl_state; + data->timer_type = timer_type; + data->timeout_type = timeout_type; + data->pcc_id = pcc_id; + data->payload = payload; + + thread_add_timer(ctrl_state->self, timer_cb, (void *)data, delay, + thread); + + return 0; +} + +int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timer_type timer_type, + enum pcep_ctrl_timeout_type timeout_type, + uint32_t delay, void *payload, struct thread **thread) +{ + return schedule_thread_timer_with_cb(ctrl_state, pcc_id, timer_type, + timeout_type, delay, payload, + thread, pcep_thread_timer_handler); +} + +int pcep_thread_timer_handler(struct thread *thread) +{ + /* data unpacking */ + struct pcep_ctrl_timer_data *data = THREAD_ARG(thread); + assert(data != NULL); + struct ctrl_state *ctrl_state = data->ctrl_state; + assert(ctrl_state != NULL); + enum pcep_ctrl_timer_type timer_type = data->timer_type; + enum pcep_ctrl_timeout_type timeout_type = data->timeout_type; + int pcc_id = data->pcc_id; + void *param = data->payload; + XFREE(MTYPE_PCEP, data); + + int ret = 0; + struct pcc_state *pcc_state = NULL; + + switch (timer_type) { + case TM_RECONNECT_PCC: + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + if (!pcc_state) + return ret; + pcep_pcc_reconnect(ctrl_state, pcc_state); + break; + case TM_TIMEOUT: + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + if (!pcc_state) + return ret; + pcep_pcc_timeout_handler(ctrl_state, pcc_state, timeout_type, + param); + break; + case TM_CALCULATE_BEST_PCE: + /* Previous best disconnect so new best should be synced */ + ret = pcep_pcc_timer_update_best_pce(ctrl_state, pcc_id); + break; + case TM_SESSION_TIMEOUT_PCC: + pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + pcep_thread_remove_candidate_path_segments(ctrl_state, + pcc_state); + break; + default: + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Unknown controller timer triggered: %u", timer_type); + break; + } + + return ret; +} + +int pcep_thread_pcep_event(struct thread *thread) +{ + struct pcep_ctrl_event_data *data = THREAD_ARG(thread); + assert(data != NULL); + struct ctrl_state *ctrl_state = data->ctrl_state; + pcep_event *event = data->payload; + XFREE(MTYPE_PCEP, data); + int i; + + for (i = 0; i < MAX_PCC; i++) { + if (ctrl_state->pcc[i]) { + struct pcc_state *pcc_state = ctrl_state->pcc[i]; + if (pcc_state->sess != event->session) + continue; + pcep_pcc_pcep_event_handler(ctrl_state, pcc_state, + event); + break; + } + } + destroy_pcep_event(event); + + return 0; +} + +/* ------------ Controller Thread Socket Functions ------------ */ + +int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_socket_type type, bool is_read, + void *payload, int fd, struct thread **thread, + pcep_ctrl_thread_callback socket_cb) +{ + assert(thread != NULL); + + struct pcep_ctrl_socket_data *data; + + data = XCALLOC(MTYPE_PCEP, sizeof(*data)); + data->ctrl_state = ctrl_state; + data->type = type; + data->is_read = is_read; + data->fd = fd; + data->pcc_id = pcc_id; + data->payload = payload; + + if (is_read) { + thread_add_read(ctrl_state->self, socket_cb, (void *)data, fd, + thread); + } else { + thread_add_write(ctrl_state->self, socket_cb, (void *)data, fd, + thread); + } + + return 0; +} + +int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload, + pcep_ctrl_thread_callback socket_cb) +{ + struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data; + + return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, false, + payload, fd, (struct thread **)thread, + socket_cb); +} + +int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload, + pcep_ctrl_thread_callback socket_cb) +{ + struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data; + + return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, true, + payload, fd, (struct thread **)thread, + socket_cb); +} + +int pcep_thread_send_ctrl_event(void *fpt, void *payload, + pcep_ctrl_thread_callback cb) +{ + struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data; + + return send_to_thread_with_cb(ctrl_state, 0, EV_PCEPLIB_EVENT, 0, + payload, cb); +} + +/* ------------ Controller Thread Event Handler ------------ */ + +int send_to_thread(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_event_type type, uint32_t sub_type, + void *payload) +{ + return send_to_thread_with_cb(ctrl_state, pcc_id, type, sub_type, + payload, pcep_thread_event_handler); +} + +int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_event_type type, uint32_t sub_type, + void *payload, pcep_ctrl_thread_callback event_cb) +{ + struct pcep_ctrl_event_data *data; + + data = XCALLOC(MTYPE_PCEP, sizeof(*data)); + data->ctrl_state = ctrl_state; + data->type = type; + data->sub_type = sub_type; + data->pcc_id = pcc_id; + data->payload = payload; + + thread_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL); + + return 0; +} + +int pcep_thread_event_handler(struct thread *thread) +{ + /* data unpacking */ + struct pcep_ctrl_event_data *data = THREAD_ARG(thread); + assert(data != NULL); + struct ctrl_state *ctrl_state = data->ctrl_state; + assert(ctrl_state != NULL); + enum pcep_ctrl_event_type type = data->type; + uint32_t sub_type = data->sub_type; + int pcc_id = data->pcc_id; + void *payload = data->payload; + XFREE(MTYPE_PCEP, data); + + int ret = 0; + + /* Possible sub-type values */ + enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED; + + /* Possible payload values */ + struct path *path = NULL; + struct pcc_opts *pcc_opts = NULL; + struct pce_opts *pce_opts = NULL; + struct pcc_state *pcc_state = NULL; + + switch (type) { + case EV_UPDATE_PCC_OPTS: + assert(payload != NULL); + pcc_opts = (struct pcc_opts *)payload; + ret = pcep_thread_event_update_pcc_options(ctrl_state, + pcc_opts); + break; + case EV_UPDATE_PCE_OPTS: + assert(payload != NULL); + pce_opts = (struct pce_opts *)payload; + ret = pcep_thread_event_update_pce_options(ctrl_state, pcc_id, + pce_opts); + break; + case EV_REMOVE_PCC: + pce_opts = (struct pce_opts *)payload; + ret = pcep_thread_event_remove_pcc(ctrl_state, pce_opts); + if (ret == 0) { + ret = pcep_pcc_multi_pce_remove_pcc(ctrl_state, + ctrl_state->pcc); + } + break; + case EV_PATHD_EVENT: + assert(payload != NULL); + path_event_type = (enum pcep_pathd_event_type)sub_type; + path = (struct path *)payload; + ret = pcep_thread_event_pathd_event(ctrl_state, path_event_type, + path); + break; + case EV_SYNC_PATH: + assert(payload != NULL); + path = (struct path *)payload; + pcep_pcc_multi_pce_sync_path(ctrl_state, pcc_id, + ctrl_state->pcc); + pcep_thread_event_sync_path(ctrl_state, pcc_id, path); + break; + case EV_SYNC_DONE: + ret = pcep_thread_event_sync_done(ctrl_state, pcc_id); + break; + case EV_RESET_PCC_SESSION: + pcc_state = pcep_pcc_get_pcc_by_name(ctrl_state->pcc, + (const char *)payload); + if (pcc_state) { + pcep_pcc_disable(ctrl_state, pcc_state); + ret = pcep_pcc_enable(ctrl_state, pcc_state); + } else { + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Cannot reset state for PCE: %s", + (const char *)payload); + } + break; + default: + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Unexpected event received in controller thread: %u", + type); + break; + } + + return ret; +} + +int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state, + struct pcc_opts *opts) +{ + assert(opts != NULL); + if (ctrl_state->pcc_opts != NULL) { + XFREE(MTYPE_PCEP, ctrl_state->pcc_opts); + } + ctrl_state->pcc_opts = opts; + return 0; +} + +int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state, + int pcc_id, struct pce_opts *pce_opts) +{ + if (!pce_opts || !ctrl_state) { + return 0; + } + struct pcc_state *pcc_state; + struct pcc_opts *pcc_opts; + + int current_pcc_id = + pcep_pcc_get_pcc_id_by_ip_port(ctrl_state->pcc, pce_opts); + if (current_pcc_id) { + pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, current_pcc_id); + } else { + pcc_state = pcep_pcc_initialize(ctrl_state, + get_next_id(ctrl_state)); + if (set_pcc_state(ctrl_state, pcc_state)) { + XFREE(MTYPE_PCEP, pcc_state); + return 0; + } + } + + /* Copy the pcc options to delegate it to the update function */ + pcc_opts = XCALLOC(MTYPE_PCEP, sizeof(*pcc_opts)); + memcpy(pcc_opts, ctrl_state->pcc_opts, sizeof(*pcc_opts)); + + if (pcep_pcc_update(ctrl_state, pcc_state, pcc_opts, pce_opts)) { + flog_err(EC_PATH_PCEP_PCC_CONF_UPDATE, + "failed to update PCC configuration"); + } + + + return 0; +} + +int pcep_thread_event_remove_pcc_by_id(struct ctrl_state *ctrl_state, + int pcc_id) +{ + if (pcc_id) { + struct pcc_state *pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + if (pcc_state) { + remove_pcc_state(ctrl_state, pcc_state); + pcep_pcc_finalize(ctrl_state, pcc_state); + } + } + return 0; +} + +int pcep_thread_event_remove_pcc_all(struct ctrl_state *ctrl_state) +{ + assert(ctrl_state != NULL); + + for (int i = 0; i < MAX_PCC; i++) { + pcep_thread_event_remove_pcc_by_id( + ctrl_state, + pcep_pcc_get_pcc_id_by_idx(ctrl_state->pcc, i)); + } + return 0; +} + +int pcep_thread_event_remove_pcc(struct ctrl_state *ctrl_state, + struct pce_opts *pce_opts) +{ + assert(ctrl_state != NULL); + + if (pce_opts) { + int pcc_id = pcep_pcc_get_pcc_id_by_ip_port(ctrl_state->pcc, + pce_opts); + if (pcc_id) { + pcep_thread_event_remove_pcc_by_id(ctrl_state, pcc_id); + } else { + return -1; + } + XFREE(MTYPE_PCEP, pce_opts); + } else { + pcep_thread_event_remove_pcc_all(ctrl_state); + } + + return 0; +} + +int pcep_thread_event_sync_path(struct ctrl_state *ctrl_state, int pcc_id, + struct path *path) +{ + struct pcc_state *pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + pcep_pcc_sync_path(ctrl_state, pcc_state, path); + pcep_free_path(path); + return 0; +} + +int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state, int pcc_id) +{ + struct pcc_state *pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id); + pcep_pcc_sync_done(ctrl_state, pcc_state); + return 0; +} + +int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state, + enum pcep_pathd_event_type type, + struct path *path) +{ + int i; + + for (i = 0; i < MAX_PCC; i++) { + if (ctrl_state->pcc[i]) { + struct pcc_state *pcc_state = ctrl_state->pcc[i]; + pcep_pcc_pathd_event_handler(ctrl_state, pcc_state, + type, path); + } + } + + pcep_free_path(path); + + return 0; +} + + +/* ------------ Main Thread Event Handler ------------ */ + +int send_to_main(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_main_event_type type, void *payload) +{ + struct pcep_main_event_data *data; + + data = XCALLOC(MTYPE_PCEP, sizeof(*data)); + data->handler = ctrl_state->main_event_handler; + data->type = type; + data->pcc_id = pcc_id; + data->payload = payload; + + thread_add_event(ctrl_state->main, pcep_main_event_handler, + (void *)data, 0, NULL); + return 0; +} + +int pcep_main_event_handler(struct thread *thread) +{ + /* data unpacking */ + struct pcep_main_event_data *data = THREAD_ARG(thread); + assert(data != NULL); + pcep_main_event_handler_t handler = data->handler; + enum pcep_main_event_type type = data->type; + int pcc_id = data->pcc_id; + void *payload = data->payload; + XFREE(MTYPE_PCEP, data); + + return handler(type, pcc_id, payload); +} + + +/* ------------ Helper functions ------------ */ +void set_ctrl_state(struct frr_pthread *fpt, struct ctrl_state *ctrl_state) +{ + assert(fpt != NULL); + fpt->data = ctrl_state; +} + +struct ctrl_state *get_ctrl_state(struct frr_pthread *fpt) +{ + assert(fpt != NULL); + assert(fpt->data != NULL); + + struct ctrl_state *ctrl_state; + ctrl_state = (struct ctrl_state *)fpt->data; + assert(ctrl_state != NULL); + return ctrl_state; +} + +int get_next_id(struct ctrl_state *ctrl_state) +{ + return ++ctrl_state->pcc_last_id; +} + +int set_pcc_state(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) +{ + assert(ctrl_state != NULL); + assert(pcep_pcc_get_pcc_id(pcc_state) != 0); + + int current_pcc_idx = pcep_pcc_get_free_pcc_idx(ctrl_state->pcc); + if (current_pcc_idx >= 0) { + ctrl_state->pcc[current_pcc_idx] = pcc_state; + ctrl_state->pcc_count++; + PCEP_DEBUG("added pce pcc_id (%d) idx (%d)", + pcep_pcc_get_pcc_id(pcc_state), current_pcc_idx); + return 0; + } else { + PCEP_DEBUG("Max number of pce "); + return 1; + } +} + +void remove_pcc_state(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + assert(ctrl_state != NULL); + assert(pcep_pcc_get_pcc_id(pcc_state) != 0); + + int idx = 0; + idx = pcep_pcc_get_pcc_idx_by_id(ctrl_state->pcc, + pcep_pcc_get_pcc_id(pcc_state)); + if (idx != -1) { + ctrl_state->pcc[idx] = NULL; + ctrl_state->pcc_count--; + PCEP_DEBUG("removed pce pcc_id (%d)", + pcep_pcc_get_pcc_id(pcc_state)); + } +} + +uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t retry_count) +{ + uint32_t a = min(max, base * (1 << retry_count)); + uint64_t r = rand(), m = RAND_MAX; + uint32_t b = (a / 2) + (r * (a / 2)) / m; + return b; +} + +const char *timer_type_name(enum pcep_ctrl_timer_type type) +{ + switch (type) { + case TM_UNDEFINED: + return "UNDEFINED"; + case TM_RECONNECT_PCC: + return "RECONNECT_PCC"; + case TM_PCEPLIB_TIMER: + return "PCEPLIB_TIMER"; + case TM_TIMEOUT: + return "TIMEOUT"; + default: + return "UNKNOWN"; + } +}; + +const char *timeout_type_name(enum pcep_ctrl_timeout_type type) +{ + switch (type) { + case TO_UNDEFINED: + return "UNDEFINED"; + case TO_COMPUTATION_REQUEST: + return "COMPUTATION_REQUEST"; + default: + return "UNKNOWN"; + } +} diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h new file mode 100644 index 000000000..8f25ccc1e --- /dev/null +++ b/pathd/path_pcep_controller.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_CONTROLLER_H_ +#define _PATH_PCEP_CONTROLLER_H_ + +#include "pathd/path_pcep.h" + + +enum pcep_main_event_type { + PCEP_MAIN_EVENT_UNDEFINED = 0, + PCEP_MAIN_EVENT_START_SYNC, + PCEP_MAIN_EVENT_UPDATE_CANDIDATE, + PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP +}; + +typedef int (*pcep_main_event_handler_t)(enum pcep_main_event_type type, + int pcc_id, void *payload); + +enum pcep_pathd_event_type { + PCEP_PATH_UNDEFINED = 0, + PCEP_PATH_CREATED, + PCEP_PATH_UPDATED, + PCEP_PATH_REMOVED +}; + +struct ctrl_state { + struct thread_master *main; + struct thread_master *self; + pcep_main_event_handler_t main_event_handler; + struct pcc_opts *pcc_opts; + int pcc_count; + int pcc_last_id; + struct pcc_state *pcc[MAX_PCC]; +}; + +/* Timer handling data structures */ + +enum pcep_ctrl_timeout_type { TO_UNDEFINED, TO_COMPUTATION_REQUEST, TO_MAX }; + +enum pcep_ctrl_timer_type { + TM_UNDEFINED, + TM_RECONNECT_PCC, + TM_PCEPLIB_TIMER, + TM_TIMEOUT, + TM_CALCULATE_BEST_PCE, + TM_SESSION_TIMEOUT_PCC, + TM_MAX +}; + +struct pcep_ctrl_timer_data { + struct ctrl_state *ctrl_state; + enum pcep_ctrl_timer_type timer_type; + enum pcep_ctrl_timeout_type timeout_type; + int pcc_id; + void *payload; +}; + +/* Socket handling data structures */ + +enum pcep_ctrl_socket_type { SOCK_PCEPLIB = 1 }; + +struct pcep_ctrl_socket_data { + struct ctrl_state *ctrl_state; + enum pcep_ctrl_socket_type type; + bool is_read; + int fd; + int pcc_id; + void *payload; +}; + +typedef int (*pcep_ctrl_thread_callback)(struct thread *); + +/* PCC connection information, populated in a thread-safe + * manner with pcep_ctrl_get_pcc_info() */ +struct pcep_pcc_info { + struct ctrl_state *ctrl_state; /* will be NULL when returned */ + char pce_name[64]; + int pcc_id; + struct ipaddr pcc_addr; + uint16_t pcc_port; + int status; + short msd; + uint32_t next_reqid; + uint32_t next_plspid; + bool is_best_multi_pce; + uint8_t precedence; +}; + +/* Functions called from the main thread */ +int pcep_ctrl_initialize(struct thread_master *main_thread, + struct frr_pthread **fpt, + pcep_main_event_handler_t event_handler); +int pcep_ctrl_finalize(struct frr_pthread **fpt); +int pcep_ctrl_update_pcc_options(struct frr_pthread *fpt, + struct pcc_opts *opts); +int pcep_ctrl_update_pce_options(struct frr_pthread *fpt, + struct pce_opts *opts); +int pcep_ctrl_remove_pcc(struct frr_pthread *fpt, struct pce_opts *pce_opts); +int pcep_ctrl_reset_pcc_session(struct frr_pthread *fpt, char *pce_name); +int pcep_ctrl_pathd_event(struct frr_pthread *fpt, + enum pcep_pathd_event_type type, struct path *path); +int pcep_ctrl_sync_path(struct frr_pthread *fpt, int pcc_id, struct path *path); +int pcep_ctrl_sync_done(struct frr_pthread *fpt, int pcc_id); +struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt, + int pcc_id); +pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id); +struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt, + const char *pce_name); + +/* Synchronously send a report, the caller is responsible to free the path, + * If `pcc_id` is `0` the report is sent by all PCCs */ +void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, + struct path *path); + +/* Functions called from the controller thread */ +void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id); +void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id, + struct path *path); +void pcep_thread_cancel_timer(struct thread **thread); +void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, + int retry_count, struct thread **thread); +void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id, + enum pcep_ctrl_timeout_type type, + uint32_t delay, void *param, + struct thread **thread); +void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state, + int pcc_id, int delay, + struct thread **thread); +void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); + +void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state, + int pcc_id, int delay, + struct thread **thread); +void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state, + int delay, void *payload, + struct thread **thread, + pcep_ctrl_thread_callback cb); +int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload, + pcep_ctrl_thread_callback cb); +int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload, + pcep_ctrl_thread_callback cb); + +int pcep_thread_send_ctrl_event(void *fpt, void *payload, + pcep_ctrl_thread_callback cb); +int pcep_thread_pcep_event(struct thread *thread); +int pcep_thread_pcc_count(struct ctrl_state *ctrl_state); + +#endif // _PATH_PCEP_CONTROLLER_H_ diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c new file mode 100644 index 000000000..bcaadfe4d --- /dev/null +++ b/pathd/path_pcep_debug.c @@ -0,0 +1,1771 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdbool.h> +#include <time.h> +#include <libyang/libyang.h> + +#include "printfrr.h" +#include "ipaddr.h" + +#include "pathd/path_pcep_debug.h" + +static void _format_pcc_opts(int ps, struct pcc_opts *ops); +static void _format_pce_opts(int ps, struct pce_opts *ops); +static void _format_pcc_caps(int ps, struct pcep_caps *caps); +static void _format_pcc_state(int ps, struct pcc_state *state); +static void _format_ctrl_state(int ps, struct ctrl_state *state); +static void _format_path(int ps, struct path *path); +static void _format_path_hop(int ps, struct path_hop *hop); +static void _format_path_metric(int ps, struct path_metric *metric); +static void _format_pcep_event(int ps, pcep_event *event); +static void _format_pcep_message(int ps, struct pcep_message *msg); +static void _format_pcep_objects(int ps, double_linked_list *objs); +static void _format_pcep_object(int ps, struct pcep_object_header *obj); +static void _format_pcep_object_details(int ps, struct pcep_object_header *obj); +static void _format_pcep_object_error(int ps, struct pcep_object_error *obj); +static void _format_pcep_object_open(int ps, struct pcep_object_open *obj); +static void _format_pcep_object_rp(int ps, struct pcep_object_rp *obj); +static void _format_pcep_object_srp(int ps, struct pcep_object_srp *obj); +static void _format_pcep_object_lsp(int psps, struct pcep_object_lsp *obj); +static void _format_pcep_object_lspa(int psps, struct pcep_object_lspa *obj); +static void +_format_pcep_object_ipv4_endpoint(int ps, + struct pcep_object_endpoints_ipv4 *obj); +static void _format_pcep_object_metric(int ps, struct pcep_object_metric *obj); +static void _format_pcep_object_bandwidth(int ps, + struct pcep_object_bandwidth *obj); +static void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj); +static void +_format_pcep_object_objfun(int ps, struct pcep_object_objective_function *obj); +static void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj); +static void _format_pcep_object_ro_details(int ps, + struct pcep_object_ro_subobj *ro); +static void _format_pcep_object_ro_ipv4(int ps, + struct pcep_ro_subobj_ipv4 *obj); +static void _format_pcep_object_ro_sr(int ps, struct pcep_ro_subobj_sr *obj); +static void _format_pcep_object_tlvs(int ps, struct pcep_object_header *obj); +static void _format_pcep_object_tlv(int ps, + struct pcep_object_tlv_header *tlv_header); +static void +_format_pcep_object_tlv_details(int ps, + struct pcep_object_tlv_header *tlv_header); +static void _format_pcep_object_tlv_symbolic_path_name( + int ps, struct pcep_object_tlv_symbolic_path_name *tlv); +static void _format_pcep_object_tlv_stateful_pce_capability( + int ps, struct pcep_object_tlv_stateful_pce_capability *tlv); +static void _format_pcep_object_tlv_sr_pce_capability( + int ps, struct pcep_object_tlv_sr_pce_capability *tlv); +static void _format_pcep_object_tlv_path_setup_type( + int ps, struct pcep_object_tlv_path_setup_type *tlv); + +const char *pcc_status_name(enum pcc_status status) +{ + switch (status) { + case PCEP_PCC_INITIALIZED: + return "INITIALIZED"; + case PCEP_PCC_DISCONNECTED: + return "DISCONNECTED"; + case PCEP_PCC_CONNECTING: + return "CONNECTING"; + case PCEP_PCC_SYNCHRONIZING: + return "SYNCHRONIZING"; + case PCEP_PCC_OPERATING: + return "OPERATING"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_event_type_name(pcep_event_type event_type) +{ + switch (event_type) { + case MESSAGE_RECEIVED: + return "MESSAGE_RECEIVED"; + case PCE_CLOSED_SOCKET: + return "PCE_CLOSED_SOCKET"; + case PCE_SENT_PCEP_CLOSE: + return "PCE_SENT_PCEP_CLOSE"; + case PCE_DEAD_TIMER_EXPIRED: + return "PCE_DEAD_TIMER_EXPIRED"; + case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED: + return "PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED"; + case PCC_CONNECTED_TO_PCE: + return "PCC_CONNECTED_TO_PCE"; + case PCC_PCEP_SESSION_CLOSED: + return "PCC_PCEP_SESSION_CLOSED"; + case PCC_RCVD_INVALID_OPEN: + return "PCC_RCVD_INVALID_OPEN"; + case PCC_RCVD_MAX_INVALID_MSGS: + return "PCC_RCVD_MAX_INVALID_MSGS"; + case PCC_RCVD_MAX_UNKOWN_MSGS: + return "PCC_RCVD_MAX_UNKOWN_MSGS"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_error_type_name(enum pcep_error_type error_type) +{ + switch (error_type) { + + case PCEP_ERRT_SESSION_FAILURE: + return "SESSION_FAILURE"; + case PCEP_ERRT_CAPABILITY_NOT_SUPPORTED: + return "CAPABILITY_NOT_SUPPORTED"; + case PCEP_ERRT_UNKNOW_OBJECT: + return "UNKNOW_OBJECT"; + case PCEP_ERRT_NOT_SUPPORTED_OBJECT: + return "NOT_SUPPORTED_OBJECT"; + case PCEP_ERRT_POLICY_VIOLATION: + return "POLICY_VIOLATION"; + case PCEP_ERRT_MANDATORY_OBJECT_MISSING: + return "MANDATORY_OBJECT_MISSING"; + case PCEP_ERRT_SYNC_PC_REQ_MISSING: + return "SYNC_PC_REQ_MISSING"; + case PCEP_ERRT_UNKNOWN_REQ_REF: + return "UNKNOWN_REQ_REF"; + case PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION: + return "ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION"; + case PCEP_ERRT_RECEPTION_OF_INV_OBJECT: + return "RECEPTION_OF_INV_OBJECT"; + case PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ: + return "UNRECOGNIZED_EXRS_SUBOBJ"; + case PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR: + return "DIFFSERV_AWARE_TE_ERROR"; + case PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR: + return "BRPC_PROC_COMPLETION_ERROR"; + case PCEP_ERRT_UNASSIGNED14: + return "UNASSIGNED14"; + case PCEP_ERRT_GLOBAL_CONCURRENT_ERROR: + return "GLOBAL_CONCURRENT_ERROR"; + case PCEP_ERRT_P2PMP_CAP_ERROR: + return "P2PMP_CAP_ERROR"; + case PCEP_ERRT_P2P_ENDPOINTS_ERROR: + return "P2P_ENDPOINTS_ERROR"; + case PCEP_ERRT_P2P_FRAGMENTATION_ERROR: + return "P2P_FRAGMENTATION_ERROR"; + case PCEP_ERRT_INVALID_OPERATION: + return "INVALID_OPERATION"; + case PCEP_ERRT_LSP_STATE_SYNC_ERROR: + return "LSP_STATE_SYNC_ERROR"; + case PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE: + return "INVALID_TE_PATH_SETUP_TYPE"; + case PCEP_ERRT_UNASSIGNED22: + return "UNASSIGNED22"; + case PCEP_ERRT_BAD_PARAMETER_VALUE: + return "BAD_PARAMETER_VALUE"; + case PCEP_ERRT_LSP_INSTANTIATE_ERROR: + return "LSP_INSTANTIATE_ERROR"; + case PCEP_ERRT_START_TLS_FAILURE: + return "START_TLS_FAILURE"; + case PCEP_ERRT_ASSOCIATION_ERROR: + return "ASSOCIATION_ERROR"; + case PCEP_ERRT_WSON_RWA_ERROR: + return "WSON_RWA_ERROR"; + case PCEP_ERRT_H_PCE_ERROR: + return "H_PCE_ERROR"; + case PCEP_ERRT_PATH_COMP_FAILURE: + return "PATH_COMP_FAILURE"; + case PCEP_ERRT_UNASSIGNED30: + return "UNASSIGNED30"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_error_value_name(enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + switch (TUP(error_type, error_value)) { + + case TUP(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, PCEP_ERRV_UNASSIGNED): + case TUP(PCEP_ERRT_SYNC_PC_REQ_MISSING, PCEP_ERRV_UNASSIGNED): + case TUP(PCEP_ERRT_UNKNOWN_REQ_REF, PCEP_ERRV_UNASSIGNED): + case TUP(PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION, + PCEP_ERRV_UNASSIGNED): + case TUP(PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ, PCEP_ERRV_UNASSIGNED): + return "UNASSIGNED"; + + case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_RECVD_INVALID_OPEN_MSG): + return "RECVD_INVALID_OPEN_MSG"; + case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_OPENWAIT_TIMED_OUT): + return "OPENWAIT_TIMED_OUT"; + case TUP(PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NO_NEG): + return "UNACCEPTABLE_OPEN_MSG_NO_NEG"; + case TUP(PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG): + return "UNACCEPTABLE_OPEN_MSG_NEG"; + case TUP(PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE): + return "RECVD_SECOND_OPEN_MSG_UNACCEPTABLE"; + case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_RECVD_PCERR): + return "RECVD_PCERR"; + case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT): + return "KEEPALIVEWAIT_TIMED_OUT"; + case TUP(PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_PCEP_VERSION_NOT_SUPPORTED): + return "PCEP_VERSION_NOT_SUPPORTED"; + + case TUP(PCEP_ERRT_UNKNOW_OBJECT, PCEP_ERRV_UNREC_OBJECT_CLASS): + return "UNREC_OBJECT_CLASS"; + case TUP(PCEP_ERRT_UNKNOW_OBJECT, PCEP_ERRV_UNREC_OBJECT_TYPE): + return "UNREC_OBJECT_TYPE"; + + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_NOT_SUPPORTED_OBJECT_CLASS): + return "NOT_SUPPORTED_OBJECT_CLASS"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_NOT_SUPPORTED_OBJECT_TYPE): + return "NOT_SUPPORTED_OBJECT_TYPE"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, PCEP_ERRV_UNSUPPORTED_PARAM): + return "UNSUPPORTED_PARAM"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_UNSUPPORTED_NW_PERF_CONSTRAINT): + return "UNSUPPORTED_NW_PERF_CONSTRAINT"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_NOT_SUPPORTED_BW_OBJECT_3_4): + return "NOT_SUPPORTED_BW_OBJECT_3_4"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_UNSUPPORTED_ENDPOINT_TYPE): + return "UNSUPPORTED_ENDPOINT_TYPE"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_UNSUPPORTED_ENDPOINT_TLV): + return "UNSUPPORTED_ENDPOINT_TLV"; + case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, + PCEP_ERRV_UNSUPPORTED_RP_FLAG_GRANULARITY): + return "UNSUPPORTED_RP_FLAG_GRANULARITY"; + + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_C_BIT_SET_IN_METRIC_OBJECT): + return "C_BIT_SET_IN_METRIC_OBJECT"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_O_BIT_CLEARD_IN_RP_OBJECT): + return "O_BIT_CLEARD_IN_RP_OBJECT"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_OBJECTIVE_FUNC_NOT_ALLOWED): + return "OBJECTIVE_FUNC_NOT_ALLOWED"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, PCEP_ERRV_RP_OF_BIT_SET): + return "RP_OF_BIT_SET"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_GLOBAL_CONCURRENCY_NOT_ALLOWED): + return "GLOBAL_CONCURRENCY_NOT_ALLOWED"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, PCEP_ERRV_MONITORING_MSG_REJECTED): + return "MONITORING_MSG_REJECTED"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_P2MP_PATH_COMP_NOT_ALLOWED): + return "P2MP_PATH_COMP_NOT_ALLOWED"; + case TUP(PCEP_ERRT_POLICY_VIOLATION, + PCEP_ERRV_UNALLOWED_NW_PERF_CONSTRAINT): + return "UNALLOWED_NW_PERF_CONSTRAINT"; + + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_RP_OBJECT_MISSING): + return "RP_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_RRO_OBJECT_MISSING_FOR_REOP): + return "RRO_OBJECT_MISSING_FOR_REOP"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_EP_OBJECT_MISSING): + return "EP_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_MONITOR_OBJECT_MISSING): + return "MONITOR_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_OBJECT_MISSING): + return "LSP_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_ERO_OBJECT_MISSING): + return "ERO_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING): + return "SRP_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_ID_TLV_MISSING): + return "LSP_ID_TLV_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_DB_TLV_MISSING): + return "LSP_DB_TLV_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_S2LS_OBJECT_MISSING): + return "S2LS_OBJECT_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_P2MP_LSP_ID_TLV_MISSING): + return "P2MP_LSP_ID_TLV_MISSING"; + case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING): + return "DISJOINTED_CONF_TLV_MISSING"; + + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_P_FLAG_NOT_CORRECT_IN_OBJECT): + return "P_FLAG_NOT_CORRECT_IN_OBJECT"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_BAD_LABEL_VALUE): + return "BAD_LABEL_VALUE"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS): + return "UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_BAD_LABEL_FORMAT): + return "BAD_LABEL_FORMAT"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_ERO_SR_ERO_MIX): + return "ERO_SR_ERO_MIX"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_SR_ERO_SID_NAI_ABSENT): + return "SR_ERO_SID_NAI_ABSENT"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_SR_RRO_SID_NAI_ABSENT): + return "SR_RRO_SID_NAI_ABSENT"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING): + return "SYMBOLIC_PATH_NAME_TLV_MISSING"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_MSD_EXCEEDS_PCEP_SESSION_MAX): + return "MSD_EXCEEDS_PCEP_SESSION_MAX"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_RRO_SR_RRO_MIX): + return "RRO_SR_RRO_MIX"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_MALFORMED_OBJECT): + return "MALFORMED_OBJECT"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_MISSING_PCE_SR_CAP_TLV): + return "MISSING_PCE_SR_CAP_TLV"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_UNSUPPORTED_NAI): + return "UNSUPPORTED_NAI"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_UNKNOWN_SID): + return "UNKNOWN_SID"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_CANNOT_RESOLVE_NAI_TO_SID): + return "CANNOT_RESOLVE_NAI_TO_SID"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_COULD_NOT_FIND_SRGB): + return "COULD_NOT_FIND_SRGB"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_SID_EXCEEDS_SRGB): + return "SID_EXCEEDS_SRGB"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_COULD_NOT_FIND_SRLB): + return "COULD_NOT_FIND_SRLB"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_SID_EXCEEDS_SRLB): + return "SID_EXCEEDS_SRLB"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_INCONSISTENT_SID): + return "INCONSISTENT_SID"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_MSD_MUST_BE_NONZERO): + return "MSD_MUST_BE_NONZERO"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_MISMATCH_O_S2LS_LSP): + return "MISMATCH_O_S2LS_LSP"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_INCOMPATIBLE_H_PCE_OF): + return "INCOMPATIBLE_H_PCE_OF"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_BAD_BANDWIDTH_TYPE_3_4): + return "BAD_BANDWIDTH_TYPE_3_4"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_UNSUPPORTED_LSP_PROT_FLAGS): + return "UNSUPPORTED_LSP_PROT_FLAGS"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_UNSUPPORTED_2ND_LSP_PROT_FLAGS): + return "UNSUPPORTED_2ND_LSP_PROT_FLAGS"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_UNSUPPORTED_LINK_PROT_TYPE): + return "UNSUPPORTED_LINK_PROT_TYPE"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_LABEL_SET_TLV_NO_RP_R): + return "LABEL_SET_TLV_NO_RP_R"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_WRONG_LABEL_SET_TLV_O_L_SET): + return "WRONG_LABEL_SET_TLV_O_L_SET"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_WRONG_LABEL_SET_O_SET): + return "WRONG_LABEL_SET_O_SET"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_MISSING_GMPLS_CAP_TLV): + return "MISSING_GMPLS_CAP_TLV"; + case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, + PCEP_ERRV_INCOMPATIBLE_OF_CODE): + return "INCOMPATIBLE_OF_CODE"; + + case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR, + PCEP_ERRV_UNSUPPORTED_CLASS_TYPE): + return "UNSUPPORTED_CLASS_TYPE"; + case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR, + PCEP_ERRV_INVALID_CLASS_TYPE): + return "INVALID_CLASS_TYPE"; + case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR, + PCEP_ERRV_CLASS_SETUP_TYPE_NOT_TE_CLASS): + return "CLASS_SETUP_TYPE_NOT_TE_CLASS"; + + case TUP(PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR, + PCEP_ERRV_BRPC_PROC_NOT_SUPPORTED): + return "BRPC_PROC_NOT_SUPPORTED"; + + case TUP(PCEP_ERRT_GLOBAL_CONCURRENT_ERROR, + PCEP_ERRV_INSUFFICIENT_MEMORY): + return "INSUFFICIENT_MEMORY"; + case TUP(PCEP_ERRT_GLOBAL_CONCURRENT_ERROR, + PCEP_ERRV_GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED): + return "GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED"; + + case TUP(PCEP_ERRT_P2PMP_CAP_ERROR, PCEP_ERRV_PCE_INSUFFICIENT_MEMORY): + return "PCE_INSUFFICIENT_MEMORY"; + case TUP(PCEP_ERRT_P2PMP_CAP_ERROR, + PCEP_ERRV_PCE_NOT_CAPABLE_P2MP_COMP): + return "PCE_NOT_CAPABLE_P2MP_COMP"; + + case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR, + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE2): + return "NO_EP_WITH_LEAF_TYPE2"; + case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR, + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE3): + return "NO_EP_WITH_LEAF_TYPE3"; + case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR, + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE4): + return "NO_EP_WITH_LEAF_TYPE4"; + case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR, PCEP_ERRV_INCONSITENT_EP): + return "INCONSITENT_EP"; + + case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR, + PCEP_ERRV_FRAG_REQUEST_FAILURE): + return "FRAG_REQUEST_FAILURE"; + case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR, + PCEP_ERRV_FRAG_REPORT_FAILURE): + return "FRAG_REPORT_FAILURE"; + case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR, + PCEP_ERRV_FRAG_UPDATE_FAILURE): + return "FRAG_UPDATE_FAILURE"; + case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR, + PCEP_ERRV_FRAG_INSTANTIATION_FAILURE): + return "FRAG_INSTANTIATION_FAILURE"; + + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP): + return "LSP_UPDATE_FOR_NON_DELEGATED_LS"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_UPDATE_NON_ADVERTISED_PCE): + return "LSP_UPDATE_NON_ADVERTISED_PC"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID): + return "LSP_UPDATE_UNKNOWN_PLSP_I"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_REPORT_NON_ADVERTISED_PCE): + return "LSP_REPORT_NON_ADVERTISED_PC"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_PCE_INIT_LSP_LIMIT_REACHED): + return "PCE_INIT_LSP_LIMIT_REACHE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_PCE_INIT_LSP_DELEGATION_CANT_REVOKE): + return "PCE_INIT_LSP_DELEGATION_CANT_REVOK"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID): + return "LSP_INIT_NON_ZERO_PLSP_I"; + case TUP(PCEP_ERRT_INVALID_OPERATION, PCEP_ERRV_LSP_NOT_PCE_INITIATED): + return "LSP_NOT_PCE_INITIATE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_PCE_INIT_OP_FREQ_LIMIT_REACHED): + return "PCE_INIT_OP_FREQ_LIMIT_REACHE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_REPORT_P2MP_NOT_ADVERTISED): + return "LSP_REPORT_P2MP_NOT_ADVERTISE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_UPDATE_P2MP_NOT_ADVERTISED): + return "LSP_UPDATE_P2MP_NOT_ADVERTISE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_INSTANTIATION_P2MP_NOT_ADVERTISED): + return "LSP_INSTANTIATION_P2MP_NOT_ADVERTISE"; + case TUP(PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_AUTO_BW_CAP_NOT_ADVERTISED): + return "AUTO_BW_CAP_NOT_ADVERTISE"; + + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_PCE_CANT_PROCESS_LSP_REPORT): + return "PCE_CANT_PROCESS_LSP_REPORT"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_LSP_DB_VERSION_MISMATCH): + return "LSP_DB_VERSION_MISMATCH"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER): + return "TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP): + return "TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_PCC_CANT_COMPLETE_STATE_SYNC): + return "PCC_CANT_COMPLETE_STATE_SYNC"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_INVALID_LSP_DB_VERSION_NUMBER): + return "INVALID_LSP_DB_VERSION_NUMBER"; + case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR, + PCEP_ERRV_INVALID_SPEAKER_ENTITY_ID): + return "INVALID_SPEAKER_ENTITY_ID"; + + case TUP(PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE, + PCEP_ERRV_UNSUPPORTED_PATH_SETUP_TYPE): + return "UNSUPPORTED_PATH_SETUP_TYPE"; + case TUP(PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE, + PCEP_ERRV_MISMATCHED_PATH_SETUP_TYPE): + return "MISMATCHED_PATH_SETUP_TYPE"; + + case TUP(PCEP_ERRT_BAD_PARAMETER_VALUE, + PCEP_ERRV_SYMBOLIC_PATH_NAME_IN_USE): + return "SYMBOLIC_PATH_NAME_IN_USE"; + case TUP(PCEP_ERRT_BAD_PARAMETER_VALUE, + PCEP_ERRV_LSP_SPEAKER_ID_NOT_PCE_INITIATED): + return "LSP_SPEAKER_ID_NOT_PCE_INITIATED"; + + case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR, + PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR): + return "UNACCEPTABLE_INSTANTIATE_ERROR"; + case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR, PCEP_ERRV_INTERNAL_ERROR): + return "INTERNAL_ERROR"; + case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR, PCEP_ERRV_SIGNALLING_ERROR): + return "SIGNALLING_ERROR"; + + case TUP(PCEP_ERRT_START_TLS_FAILURE, + PCEP_ERRV_START_TLS_AFTER_PCEP_EXCHANGE): + return "START_TLS_AFTER_PCEP_EXCHANGE"; + case TUP(PCEP_ERRT_START_TLS_FAILURE, + PCEP_ERRV_MSG_NOT_START_TLS_OPEN_ERROR): + return "MSG_NOT_START_TLS_OPEN_ERROR"; + case TUP(PCEP_ERRT_START_TLS_FAILURE, + PCEP_ERRV_CONNECTION_WO_TLS_NOT_POSSIBLE): + return "CONNECTION_WO_TLS_NOT_POSSIBLE"; + case TUP(PCEP_ERRT_START_TLS_FAILURE, + PCEP_ERRV_CONNECTION_WO_TLS_IS_POSSIBLE): + return "CONNECTION_WO_TLS_IS_POSSIBLE"; + case TUP(PCEP_ERRT_START_TLS_FAILURE, + PCEP_ERRV_NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER): + return "NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER"; + + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_ASSOC_TYPE_NOT_SUPPORTED): + return "ASSOC_TYPE_NOT_SUPPORTED"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_TOO_MANY_LSPS_IN_ASSOC_GRP): + return "TOO_MANY_LSPS_IN_ASSOC_GRP"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_TOO_MANY_ASSOC_GROUPS): + return "TOO_MANY_ASSOC_GROUPS"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOCIATION_UNKNOWN): + return "ASSOCIATION_UNKNOWN"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_OP_CONF_ASSOC_INFO_MISMATCH): + return "OP_CONF_ASSOC_INFO_MISMATCH"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOC_INFO_MISMATCH): + return "ASSOC_INFO_MISMATCH"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_CANNOT_JOIN_ASSOC_GROUP): + return "CANNOT_JOIN_ASSOC_GROUP"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOC_ID_NOT_IN_RANGE): + return "ASSOC_ID_NOT_IN_RANGE"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC): + return "TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC): + return "ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC"; + case TUP(PCEP_ERRT_ASSOCIATION_ERROR, + PCEP_ERRV_PROTECTION_TYPE_NOT_SUPPORTED): + return "PROTECTION_TYPE_NOT_SUPPORTED"; + + case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_RWA_INSUFFICIENT_MEMORY): + return "RWA_INSUFFICIENT_MEMORY"; + case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_RWA_COMP_NOT_SUPPORTED): + return "RWA_COMP_NOT_SUPPORTED"; + case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_SYNTAX_ENC_ERROR): + return "SYNTAX_ENC_ERROR"; + + case TUP(PCEP_ERRT_H_PCE_ERROR, PCEP_ERRV_H_PCE_CAP_NOT_ADVERTISED): + return "H_PCE_CAP_NOT_ADVERTISED"; + case TUP(PCEP_ERRT_H_PCE_ERROR, + PCEP_ERRV_PARENT_PCE_CAP_CANT_BE_PROVIDED): + return "PARENT_PCE_CAP_CANT_BE_PROVIDED"; + + case TUP(PCEP_ERRT_PATH_COMP_FAILURE, + PCEP_ERRV_UNACCEPTABLE_REQUEST_MSG): + return "UNACCEPTABLE_REQUEST_MSG"; + case TUP(PCEP_ERRT_PATH_COMP_FAILURE, + PCEP_ERRV_GENERALIZED_BW_VAL_NOT_SUPPORTED): + return "GENERALIZED_BW_VAL_NOT_SUPPORTED"; + case TUP(PCEP_ERRT_PATH_COMP_FAILURE, + PCEP_ERRV_LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET): + return "LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET"; + case TUP(PCEP_ERRT_PATH_COMP_FAILURE, + PCEP_ERRV_LABEL_CONSTRAINT_COULD_NOT_BE_MET): + return "LABEL_CONSTRAINT_COULD_NOT_BE_MET"; + + default: + return "UNKNOWN"; + } +} + +const char *pcep_message_type_name(enum pcep_message_types pcep_message_type) +{ + switch (pcep_message_type) { + + case PCEP_TYPE_OPEN: + return "OPEN"; + case PCEP_TYPE_KEEPALIVE: + return "KEEPALIVE"; + case PCEP_TYPE_PCREQ: + return "PCREQ"; + case PCEP_TYPE_PCREP: + return "PCREP"; + case PCEP_TYPE_PCNOTF: + return "PCNOTF"; + case PCEP_TYPE_ERROR: + return "ERROR"; + case PCEP_TYPE_CLOSE: + return "CLOSE"; + case PCEP_TYPE_REPORT: + return "REPORT"; + case PCEP_TYPE_UPDATE: + return "UPDATE"; + case PCEP_TYPE_INITIATE: + return "INITIATE"; + case PCEP_TYPE_UNKOWN_MSG: + return "UNKOWN_MSG"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_object_class_name(enum pcep_object_classes obj_class) +{ + switch (obj_class) { + case PCEP_OBJ_CLASS_OPEN: + return "OPEN"; + case PCEP_OBJ_CLASS_RP: + return "RP"; + case PCEP_OBJ_CLASS_NOPATH: + return "NOPATH"; + case PCEP_OBJ_CLASS_ENDPOINTS: + return "ENDPOINTS"; + case PCEP_OBJ_CLASS_BANDWIDTH: + return "BANDWIDTH"; + case PCEP_OBJ_CLASS_METRIC: + return "METRIC"; + case PCEP_OBJ_CLASS_ERO: + return "ERO"; + case PCEP_OBJ_CLASS_RRO: + return "RRO"; + case PCEP_OBJ_CLASS_LSPA: + return "LSPA"; + case PCEP_OBJ_CLASS_IRO: + return "IRO"; + case PCEP_OBJ_CLASS_SVEC: + return "SVEC"; + case PCEP_OBJ_CLASS_NOTF: + return "NOTF"; + case PCEP_OBJ_CLASS_ERROR: + return "ERROR"; + case PCEP_OBJ_CLASS_CLOSE: + return "CLOSE"; + case PCEP_OBJ_CLASS_OF: + return "OF"; + case PCEP_OBJ_CLASS_LSP: + return "LSP"; + case PCEP_OBJ_CLASS_SRP: + return "SRP"; + case PCEP_OBJ_CLASS_VENDOR_INFO: + return "VENDOR_INFO"; + case PCEP_OBJ_CLASS_INTER_LAYER: + return "INTER_LAYER"; + case PCEP_OBJ_CLASS_SWITCH_LAYER: + return "SWITCH_LAYER"; + case PCEP_OBJ_CLASS_REQ_ADAP_CAP: + return "REQ_ADAP_CAP"; + case PCEP_OBJ_CLASS_SERVER_IND: + return "SERVER_IND"; + case PCEP_OBJ_CLASS_ASSOCIATION: + return "ASSOCIATION"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_object_type_name(enum pcep_object_classes obj_class, + enum pcep_object_types obj_type) +{ + switch (TUP(obj_class, obj_type)) { + case TUP(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN): + return "OPEN"; + case TUP(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP): + return "RP"; + case TUP(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH): + return "NOPATH"; + case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4): + return "ENDPOINT_IPV4"; + case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV6): + return "ENDPOINT_IPV6"; + case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ): + return "BANDWIDTH_REQ"; + case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_TELSP): + return "BANDWIDTH_TELSP"; + case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_CISCO): + return "BANDWIDTH_CISCO"; + case TUP(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC): + return "METRIC"; + case TUP(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO): + return "ERO"; + case TUP(PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO): + return "RRO"; + case TUP(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA): + return "LSPA"; + case TUP(PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO): + return "IRO"; + case TUP(PCEP_OBJ_CLASS_SVEC, PCEP_OBJ_TYPE_SVEC): + return "SVEC"; + case TUP(PCEP_OBJ_CLASS_NOTF, PCEP_OBJ_TYPE_NOTF): + return "NOTF"; + case TUP(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR): + return "ERROR"; + case TUP(PCEP_OBJ_CLASS_CLOSE, PCEP_OBJ_TYPE_CLOSE): + return "CLOSE"; + case TUP(PCEP_OBJ_CLASS_INTER_LAYER, PCEP_OBJ_TYPE_INTER_LAYER): + return "INTER_LAYER"; + case TUP(PCEP_OBJ_CLASS_SWITCH_LAYER, PCEP_OBJ_TYPE_SWITCH_LAYER): + return "SWITCH_LAYER"; + case TUP(PCEP_OBJ_CLASS_REQ_ADAP_CAP, PCEP_OBJ_TYPE_REQ_ADAP_CAP): + return "REQ_ADAP_CAP"; + case TUP(PCEP_OBJ_CLASS_SERVER_IND, PCEP_OBJ_TYPE_SERVER_IND): + return "SERVER_IND"; + case TUP(PCEP_OBJ_CLASS_ASSOCIATION, PCEP_OBJ_TYPE_ASSOCIATION_IPV4): + return "ASSOCIATION_IPV4"; + case TUP(PCEP_OBJ_CLASS_ASSOCIATION, PCEP_OBJ_TYPE_ASSOCIATION_IPV6): + return "ASSOCIATION_IPV6"; + case TUP(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF): + return "OF"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_lsp_status_name(enum pcep_lsp_operational_status status) +{ + switch (status) { + case PCEP_LSP_OPERATIONAL_DOWN: + return "DOWN"; + case PCEP_LSP_OPERATIONAL_UP: + return "UP"; + case PCEP_LSP_OPERATIONAL_ACTIVE: + return "ACTIVE"; + case PCEP_LSP_OPERATIONAL_GOING_DOWN: + return "GOING_DOWN"; + case PCEP_LSP_OPERATIONAL_GOING_UP: + return "GOING_UP"; + default: + return "UNKNOWN"; + } +} + + +const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type) +{ + switch (tlv_type) { + case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR: + return "NO_PATH_VECTOR"; + case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY: + return "STATEFUL_PCE_CAPABILITY"; + case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME: + return "SYMBOLIC_PATH_NAME"; + case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS: + return "IPV4_LSP_IDENTIFIERS"; + case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS: + return "IPV6_LSP_IDENTIFIERS"; + case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE: + return "LSP_ERROR_CODE"; + case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC: + return "RSVP_ERROR_SPEC"; + case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION: + return "LSP_DB_VERSION"; + case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID: + return "SPEAKER_ENTITY_ID"; + case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY: + return "SR_PCE_CAPABILITY"; + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE: + return "PATH_SETUP_TYPE"; + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY: + return "PATH_SETUP_TYPE_CAPABILITY"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type) +{ + switch (ro_type) { + + case RO_SUBOBJ_TYPE_IPV4: + return "IPV4"; + case RO_SUBOBJ_TYPE_IPV6: + return "IPV6"; + case RO_SUBOBJ_TYPE_LABEL: + return "LABEL"; + case RO_SUBOBJ_TYPE_UNNUM: + return "UNNUM"; + case RO_SUBOBJ_TYPE_ASN: + return "ASN"; + case RO_SUBOBJ_TYPE_SR: + return "SR"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type) +{ + switch (nai_type) { + case PCEP_SR_SUBOBJ_NAI_ABSENT: + return "ABSENT"; + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + return "IPV4_NODE"; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + return "IPV6_NODE"; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + return "IPV4_ADJACENCY"; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + return "IPV6_ADJACENCY"; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + return "UNNUMBERED_IPV4_ADJACENCY"; + case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY: + return "LINK_LOCAL_IPV6_ADJACENCY"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_metric_type_name(enum pcep_metric_types type) +{ + switch (type) { + case PCEP_METRIC_IGP: + return "IGP"; + case PCEP_METRIC_TE: + return "TE"; + case PCEP_METRIC_HOP_COUNT: + return "HOP_COUNT"; + case PCEP_METRIC_AGGREGATE_BW: + return "AGGREGATE_BW"; + case PCEP_METRIC_MOST_LOADED_LINK: + return "MOST_LOADED_LINK"; + case PCEP_METRIC_CUMULATIVE_IGP: + return "CUMULATIVE_IGP"; + case PCEP_METRIC_CUMULATIVE_TE: + return "CUMULATIVE_TE"; + case PCEP_METRIC_P2MP_IGP: + return "P2MP_IGP"; + case PCEP_METRIC_P2MP_TE: + return "P2MP_TE"; + case PCEP_METRIC_P2MP_HOP_COUNT: + return "P2MP_HOP_COUNT"; + case PCEP_METRIC_SEGMENT_ID_DEPTH: + return "SEGMENT_ID_DEPTH"; + case PCEP_METRIC_PATH_DELAY: + return "PATH_DELAY"; + case PCEP_METRIC_PATH_DELAY_VARIATION: + return "PATH_DELAY_VARIATION"; + case PCEP_METRIC_PATH_LOSS: + return "PATH_LOSS"; + case PCEP_METRIC_P2MP_PATH_DELAY: + return "P2MP_PATH_DELAY"; + case PCEP_METRIC_P2MP_PATH_DELAY_VARIATION: + return "P2MP_PATH_DELAY_VARIATION"; + case PCEP_METRIC_P2MP_PATH_LOSS: + return "P2MP_PATH_LOSS"; + case PCEP_METRIC_NUM_PATH_ADAPTATIONS: + return "NUM_PATH_ADAPTATIONS"; + case PCEP_METRIC_NUM_PATH_LAYERS: + return "NUM_PATH_LAYERS"; + case PCEP_METRIC_DOMAIN_COUNT: + return "DOMAIN_COUNT"; + case PCEP_METRIC_BORDER_NODE_COUNT: + return "BORDER_NODE_COUNT"; + default: + return "UNKNOWN"; + } +} + +const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes type) +{ + switch (type) { + case PCEP_NOPATH_TLV_ERR_NO_TLV: + return "NO_TLV"; + case PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE: + return "PCE_UNAVAILABLE"; + case PCEP_NOPATH_TLV_ERR_UNKNOWN_DST: + return "UNKNOWN_DST"; + case PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC: + return "UNKNOWN_SRC"; + default: + return "UNKNOWN"; + } +} + +const char *format_objfun_set(uint32_t flags) +{ + int i, c; + PATHD_FORMAT_INIT(); + for (i = 1, c = 0; i <= MAX_OBJFUN_TYPE; i++) { + if (CHECK_FLAG(flags, i)) { + if (c > 0) + PATHD_FORMAT(", %s", objfun_type_name(i)); + else + PATHD_FORMAT("%s", objfun_type_name(i)); + c++; + } + } + return PATHD_FORMAT_FINI(); +} + + +const char *format_pcc_opts(struct pcc_opts *opts) +{ + PATHD_FORMAT_INIT(); + _format_pcc_opts(0, opts); + return PATHD_FORMAT_FINI(); +} + +const char *format_pcc_state(struct pcc_state *state) +{ + PATHD_FORMAT_INIT(); + _format_pcc_state(0, state); + return PATHD_FORMAT_FINI(); +} + +const char *format_ctrl_state(struct ctrl_state *state) +{ + PATHD_FORMAT_INIT(); + _format_ctrl_state(0, state); + return PATHD_FORMAT_FINI(); +} + +const char *format_path(struct path *path) +{ + PATHD_FORMAT_INIT(); + _format_path(0, path); + return PATHD_FORMAT_FINI(); +} + +const char *format_pcep_event(pcep_event *event) +{ + PATHD_FORMAT_INIT(); + _format_pcep_event(0, event); + return PATHD_FORMAT_FINI(); +} + +const char *format_pcep_message(struct pcep_message *msg) +{ + PATHD_FORMAT_INIT(); + _format_pcep_message(0, msg); + return PATHD_FORMAT_FINI(); +} + +const char *format_yang_dnode(struct lyd_node *dnode) +{ + char *buff; + int len; + + lyd_print_mem(&buff, dnode, LYD_JSON, LYP_FORMAT); + len = strlen(buff); + memcpy(_debug_buff, buff, len); + free(buff); + return _debug_buff; +} + +void _format_pcc_opts(int ps, struct pcc_opts *opts) +{ + if (opts == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + if (IS_IPADDR_V4(&opts->addr)) { + PATHD_FORMAT("%*saddr_v4: %pI4\n", ps2, "", + &opts->addr.ipaddr_v4); + } else { + PATHD_FORMAT("%*saddr_v4: undefined", ps2, ""); + } + if (IS_IPADDR_V6(&opts->addr)) { + PATHD_FORMAT("%*saddr_v6: %pI6\n", ps2, "", + &opts->addr.ipaddr_v6); + } else { + PATHD_FORMAT("%*saddr_v6: undefined", ps2, ""); + } + PATHD_FORMAT("%*sport: %i\n", ps2, "", opts->port); + PATHD_FORMAT("%*smsd: %i\n", ps2, "", opts->msd); + } +} + +void _format_pce_opts(int ps, struct pce_opts *opts) +{ + if (opts == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + if (IS_IPADDR_V6(&opts->addr)) { + PATHD_FORMAT("%*saddr: %pI6\n", ps2, "", + &opts->addr.ipaddr_v6); + } else { + PATHD_FORMAT("%*saddr: %pI4\n", ps2, "", + &opts->addr.ipaddr_v4); + } + PATHD_FORMAT("%*sport: %i\n", ps2, "", opts->port); + } +} + +void _format_pcc_caps(int ps, struct pcep_caps *caps) +{ + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + PATHD_FORMAT("%*sis_stateful: %d\n", ps2, "", caps->is_stateful); +} + +void _format_pcc_state(int ps, struct pcc_state *state) +{ + if (state == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + PATHD_FORMAT("%*sstatus: %s\n", ps2, "", + pcc_status_name(state->status)); + PATHD_FORMAT("%*spcc_opts: ", ps2, ""); + _format_pcc_opts(ps2, state->pcc_opts); + PATHD_FORMAT("%*spce_opts: ", ps2, ""); + _format_pce_opts(ps2, state->pce_opts); + if (state->sess == NULL) { + PATHD_FORMAT("%*ssess: NULL\n", ps2, ""); + } else { + PATHD_FORMAT("%*ssess: <PCC SESSION %p>\n", ps2, "", + state->sess); + } + PATHD_FORMAT("%*scaps: ", ps2, ""); + _format_pcc_caps(ps2, &state->caps); + } +} + +void _format_ctrl_state(int ps, struct ctrl_state *state) +{ + if (state == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int i; + int ps2 = ps + DEBUG_IDENT_SIZE; + int ps3 = ps2 + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + if (state->main == NULL) { + PATHD_FORMAT("%*smain: NULL\n", ps2, ""); + } else { + PATHD_FORMAT("%*smain: <THREAD MASTER %p>\n", ps2, "", + state->main); + } + if (state->self == NULL) { + PATHD_FORMAT("%*sself: NULL\n", ps2, ""); + } else { + PATHD_FORMAT("%*sself: <THREAD MASTER %p>\n", ps2, "", + state->self); + } + PATHD_FORMAT("%*spcc_count: %d\n", ps2, "", state->pcc_count); + PATHD_FORMAT("%*spcc:\n", ps2, ""); + for (i = 0; i < MAX_PCC; i++) { + if (state->pcc[i]) { + PATHD_FORMAT("%*s- ", ps3 - 2, ""); + _format_pcc_state(ps3, state->pcc[i]); + } + } + } +} + +void _format_path(int ps, struct path *path) +{ + if (path == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + int ps3 = ps2 + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + PATHD_FORMAT("%*snbkey: \n", ps2, ""); + PATHD_FORMAT("%*scolor: %u\n", ps3, "", path->nbkey.color); + switch (path->nbkey.endpoint.ipa_type) { + case IPADDR_V4: + PATHD_FORMAT("%*sendpoint: %pI4\n", ps3, "", + &path->nbkey.endpoint.ipaddr_v4); + break; + case IPADDR_V6: + PATHD_FORMAT("%*sendpoint: %pI6\n", ps3, "", + &path->nbkey.endpoint.ipaddr_v6); + break; + default: + PATHD_FORMAT("%*sendpoint: NONE\n", ps3, ""); + break; + } + PATHD_FORMAT("%*spreference: %u\n", ps3, "", + path->nbkey.preference); + + if (path->sender.ipa_type == IPADDR_V4) { + PATHD_FORMAT("%*ssender: %pI4\n", ps2, "", + &path->sender.ipaddr_v4); + } else if (path->sender.ipa_type == IPADDR_V6) { + PATHD_FORMAT("%*ssender: %pI6\n", ps2, "", + &path->sender.ipaddr_v6); + } else { + PATHD_FORMAT("%*ssender: UNDEFINED\n", ps2, ""); + } + if (path->pcc_addr.ipa_type == IPADDR_V4) { + PATHD_FORMAT("%*spcc_addr: %pI4\n", ps2, "", + &path->pcc_addr.ipaddr_v4); + } else if (path->pcc_addr.ipa_type == IPADDR_V6) { + PATHD_FORMAT("%*spcc_addr: %pI6\n", ps2, "", + &path->pcc_addr.ipaddr_v6); + } else { + PATHD_FORMAT("%*spcc_addr: UNDEFINED\n", ps2, ""); + } + PATHD_FORMAT("%*spcc_id: %u\n", ps2, "", path->pcc_id); + PATHD_FORMAT("%*screate_origin: %s (%u)\n", ps2, "", + srte_protocol_origin_name(path->create_origin), + path->create_origin); + PATHD_FORMAT("%*supdate_origin: %s (%u)\n", ps2, "", + srte_protocol_origin_name(path->update_origin), + path->update_origin); + if (path->originator != NULL) { + PATHD_FORMAT("%*soriginator: %s\n", ps2, "", + path->originator); + } else { + PATHD_FORMAT("%*soriginator: UNDEFINED\n", ps2, ""); + } + PATHD_FORMAT("%*stype: %s (%u)\n", ps2, "", + srte_candidate_type_name(path->type), path->type); + PATHD_FORMAT("%*splsp_id: %u\n", ps2, "", path->plsp_id); + if (path->name == NULL) { + PATHD_FORMAT("%*sname: NULL\n", ps2, ""); + } else { + PATHD_FORMAT("%*sname: %s\n", ps2, "", path->name); + } + PATHD_FORMAT("%*ssrp_id: %u\n", ps2, "", path->srp_id); + PATHD_FORMAT("%*sreq_id: %u\n", ps2, "", path->req_id); + PATHD_FORMAT("%*sstatus: %s (%u)\n", ps2, "", + pcep_lsp_status_name(path->status), path->status); + PATHD_FORMAT("%*sdo_remove: %u\n", ps2, "", path->do_remove); + PATHD_FORMAT("%*sgo_active: %u\n", ps2, "", path->go_active); + PATHD_FORMAT("%*swas_created: %u\n", ps2, "", + path->was_created); + PATHD_FORMAT("%*swas_removed: %u\n", ps2, "", + path->was_removed); + PATHD_FORMAT("%*sis_synching: %u\n", ps2, "", + path->is_synching); + PATHD_FORMAT("%*sis_delegated: %u\n", ps2, "", + path->is_delegated); + PATHD_FORMAT("%*shas_bandwidth: %u\n", ps2, "", + path->has_bandwidth); + if (path->has_bandwidth) { + PATHD_FORMAT("%*senforce_bandwidth: %u\n", ps2, "", + path->enforce_bandwidth); + PATHD_FORMAT("%*sbandwidth: %f\n", ps2, "", + path->bandwidth); + } + PATHD_FORMAT("%*shas_pcc_objfun: %u\n", ps2, "", + path->has_pcc_objfun); + if (path->has_pcc_objfun) { + PATHD_FORMAT("%*senforce_pcc_objfun: %d\n", ps2, "", + path->enforce_pcc_objfun); + PATHD_FORMAT("%*spcc_objfun: %s (%u)\n", ps2, "", + objfun_type_name(path->pcc_objfun), + path->pcc_objfun); + } + PATHD_FORMAT("%*shas_pce_objfun: %u\n", ps2, "", + path->has_pce_objfun); + if (path->has_pce_objfun) + PATHD_FORMAT("%*spce_objfun: %s (%u)\n", ps2, "", + objfun_type_name(path->pce_objfun), + path->pce_objfun); + PATHD_FORMAT("%*shas_affinity_filters: %u\n", ps2, "", + path->has_affinity_filters); + if (path->has_affinity_filters) { + PATHD_FORMAT("%*sexclude_any: 0x%08x\n", ps2, "", + path->affinity_filters + [AFFINITY_FILTER_EXCLUDE_ANY - 1]); + PATHD_FORMAT("%*sinclude_any: 0x%08x\n", ps2, "", + path->affinity_filters + [AFFINITY_FILTER_INCLUDE_ANY - 1]); + PATHD_FORMAT("%*sinclude_all: 0x%08x\n", ps2, "", + path->affinity_filters + [AFFINITY_FILTER_INCLUDE_ALL - 1]); + } + + if (path->first_hop == NULL) { + PATHD_FORMAT("%*shops: []\n", ps2, ""); + } else { + PATHD_FORMAT("%*shops: \n", ps2, ""); + for (struct path_hop *hop = path->first_hop; + hop != NULL; hop = hop->next) { + PATHD_FORMAT("%*s- ", ps3 - 2, ""); + _format_path_hop(ps3, hop); + } + } + if (path->first_metric == NULL) { + PATHD_FORMAT("%*smetrics: []\n", ps2, ""); + } else { + PATHD_FORMAT("%*smetrics: \n", ps2, ""); + for (struct path_metric *metric = path->first_metric; + NULL != metric; metric = metric->next) { + PATHD_FORMAT("%*s- ", ps3 - 2, ""); + _format_path_metric(ps3, metric); + } + } + } +} + +void _format_path_metric(int ps, struct path_metric *metric) +{ + PATHD_FORMAT("type: %s (%u)\n", pcep_metric_type_name(metric->type), + metric->type); + PATHD_FORMAT("%*senforce: %u\n", ps, "", metric->enforce); + PATHD_FORMAT("%*sis_bound: %u\n", ps, "", metric->is_bound); + PATHD_FORMAT("%*sis_computed: %u\n", ps, "", metric->is_computed); + PATHD_FORMAT("%*svalue: %f\n", ps, "", metric->value); +} + +void _format_path_hop(int ps, struct path_hop *hop) +{ + PATHD_FORMAT("is_loose: %u\n", hop->is_loose); + PATHD_FORMAT("%*shas_sid: %u\n", ps, "", hop->has_sid); + + if (hop->has_sid) { + PATHD_FORMAT("%*sis_mpls: %u\n", ps, "", hop->is_mpls); + if (hop->is_mpls) { + PATHD_FORMAT("%*shas_attribs: %u\n", ps, "", + hop->has_attribs); + PATHD_FORMAT("%*slabel: %u\n", ps, "", + hop->sid.mpls.label); + if (hop->has_attribs) { + PATHD_FORMAT("%*straffic_class: %u\n", ps, "", + hop->sid.mpls.traffic_class); + PATHD_FORMAT("%*sis_bottom: %u\n", ps, "", + hop->sid.mpls.is_bottom); + PATHD_FORMAT("%*sttl: %u\n", ps, "", + hop->sid.mpls.ttl); + } + } else { + PATHD_FORMAT("%*sSID: %u\n", ps, "", hop->sid.value); + } + } + + PATHD_FORMAT("%*shas_nai: %u\n", ps, "", hop->has_nai); + if (hop->has_nai) { + PATHD_FORMAT("%*snai_type: %s (%u)\n", ps, "", + pcep_nai_type_name(hop->nai.type), hop->nai.type); + switch (hop->nai.type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + PATHD_FORMAT("%*sNAI: %pI4\n", ps, "", + &hop->nai.local_addr.ipaddr_v4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + PATHD_FORMAT("%*sNAI: %pI6\n", ps, "", + &hop->nai.local_addr.ipaddr_v6); + break; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + PATHD_FORMAT("%*sNAI: %pI4/%pI4\n", ps, "", + &hop->nai.local_addr.ipaddr_v4, + &hop->nai.remote_addr.ipaddr_v4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + PATHD_FORMAT("%*sNAI: %pI6/%pI6\n", ps, "", + &hop->nai.local_addr.ipaddr_v6, + &hop->nai.remote_addr.ipaddr_v6); + break; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "", + &hop->nai.local_addr.ipaddr_v6, + hop->nai.local_iface, + &hop->nai.remote_addr.ipaddr_v6, + hop->nai.remote_iface); + break; + default: + PATHD_FORMAT("%*sNAI: UNSUPPORTED\n", ps, ""); + break; + } + } +} + +void _format_pcep_event(int ps, pcep_event *event) +{ + if (event == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + PATHD_FORMAT("%*sevent_type: %s\n", ps2, "", + pcep_event_type_name(event->event_type)); + PATHD_FORMAT("%*sevent_time: %s", ps2, "", + ctime(&event->event_time)); + if (event->session == NULL) { + PATHD_FORMAT("%*ssession: NULL\n", ps2, ""); + } else { + PATHD_FORMAT("%*ssession: <PCC SESSION %p>\n", ps2, "", + event->session); + } + PATHD_FORMAT("%*smessage: ", ps2, ""); + _format_pcep_message(ps2, event->message); + } +} + +void _format_pcep_message(int ps, struct pcep_message *msg) +{ + if (msg == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + int ps2 = ps + DEBUG_IDENT_SIZE; + PATHD_FORMAT("\n"); + PATHD_FORMAT("%*spcep_version: %u\n", ps2, "", + msg->msg_header->pcep_version); + PATHD_FORMAT("%*stype: %s (%u)\n", ps2, "", + pcep_message_type_name(msg->msg_header->type), + msg->msg_header->type); + PATHD_FORMAT("%*sobjects: ", ps2, ""); + _format_pcep_objects(ps2, msg->obj_list); + } +} + +void _format_pcep_objects(int ps, double_linked_list *objs) +{ + if (objs == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + double_linked_list_node *node; + int ps2 = ps + DEBUG_IDENT_SIZE; + int i; + + if (objs->num_entries == 0) { + PATHD_FORMAT("[]\n"); + return; + } + + PATHD_FORMAT("\n"); + for (node = objs->head, i = 0; node != NULL; + node = node->next_node, i++) { + struct pcep_object_header *obj = + (struct pcep_object_header *)node->data; + PATHD_FORMAT("%*s- ", ps2 - 2, ""); + _format_pcep_object(ps2, obj); + } + } +} + +void _format_pcep_object(int ps, struct pcep_object_header *obj) +{ + if (obj == NULL) { + PATHD_FORMAT("NULL\n"); + } else { + PATHD_FORMAT("object_class: %s (%u)\n", + pcep_object_class_name(obj->object_class), + obj->object_class); + PATHD_FORMAT("%*sobject_type: %s (%u)\n", ps, "", + pcep_object_type_name(obj->object_class, + obj->object_type), + obj->object_type); + PATHD_FORMAT("%*sflag_p: %u\n", ps, "", obj->flag_p); + PATHD_FORMAT("%*sflag_i: %u\n", ps, "", obj->flag_i); + _format_pcep_object_details(ps, obj); + _format_pcep_object_tlvs(ps, obj); + } +} + +void _format_pcep_object_details(int ps, struct pcep_object_header *obj) +{ + switch (TUP(obj->object_class, obj->object_type)) { + case TUP(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR): + _format_pcep_object_error(ps, (struct pcep_object_error *)obj); + break; + case TUP(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN): + _format_pcep_object_open(ps, (struct pcep_object_open *)obj); + break; + case TUP(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP): + _format_pcep_object_rp(ps, (struct pcep_object_rp *)obj); + break; + case TUP(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP): + _format_pcep_object_srp(ps, (struct pcep_object_srp *)obj); + break; + case TUP(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP): + _format_pcep_object_lsp(ps, (struct pcep_object_lsp *)obj); + break; + case TUP(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA): + _format_pcep_object_lspa(ps, (struct pcep_object_lspa *)obj); + break; + case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4): + _format_pcep_object_ipv4_endpoint( + ps, (struct pcep_object_endpoints_ipv4 *)obj); + break; + case TUP(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO): + _format_pcep_object_ro(ps, (struct pcep_object_ro *)obj); + break; + case TUP(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC): + _format_pcep_object_metric(ps, + (struct pcep_object_metric *)obj); + break; + case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ): + case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_CISCO): + _format_pcep_object_bandwidth( + ps, (struct pcep_object_bandwidth *)obj); + break; + case TUP(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH): + _format_pcep_object_nopath(ps, + (struct pcep_object_nopath *)obj); + break; + case TUP(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF): + _format_pcep_object_objfun( + ps, (struct pcep_object_objective_function *)obj); + break; + default: + PATHD_FORMAT("%*s...\n", ps, ""); + break; + } +} + +void _format_pcep_object_error(int ps, struct pcep_object_error *obj) +{ + PATHD_FORMAT("%*serror_type: %s (%u)\n", ps, "", + pcep_error_type_name(obj->error_type), obj->error_type); + PATHD_FORMAT("%*serror_value: %s (%u)\n", ps, "", + pcep_error_value_name(obj->error_type, obj->error_value), + obj->error_value); +} + + +void _format_pcep_object_open(int ps, struct pcep_object_open *obj) +{ + PATHD_FORMAT("%*sopen_version: %u\n", ps, "", obj->open_version); + PATHD_FORMAT("%*sopen_keepalive: %u\n", ps, "", obj->open_keepalive); + PATHD_FORMAT("%*sopen_deadtimer: %u\n", ps, "", obj->open_deadtimer); + PATHD_FORMAT("%*sopen_sid: %u\n", ps, "", obj->open_sid); +} + +void _format_pcep_object_rp(int ps, struct pcep_object_rp *obj) +{ + PATHD_FORMAT("%*spriority: %u\n", ps, "", obj->priority); + PATHD_FORMAT("%*sflag_reoptimization: %u\n", ps, "", + obj->flag_reoptimization); + PATHD_FORMAT("%*sflag_bidirectional: %u\n", ps, "", + obj->flag_bidirectional); + PATHD_FORMAT("%*sflag_strict: %u\n", ps, "", obj->flag_strict); + PATHD_FORMAT("%*sflag_of: %u\n", ps, "", obj->flag_of); + PATHD_FORMAT("%*srequest_id: %u\n", ps, "", obj->request_id); +} + + +void _format_pcep_object_srp(int ps, struct pcep_object_srp *obj) +{ + PATHD_FORMAT("%*sflag_lsp_remove: %u\n", ps, "", obj->flag_lsp_remove); + PATHD_FORMAT("%*ssrp_id_number: %u\n", ps, "", obj->srp_id_number); +} + +void _format_pcep_object_lsp(int ps, struct pcep_object_lsp *obj) +{ + PATHD_FORMAT("%*splsp_id: %u\n", ps, "", obj->plsp_id); + PATHD_FORMAT("%*sstatus: %s\n", ps, "", + pcep_lsp_status_name(obj->operational_status)); + PATHD_FORMAT("%*sflag_d: %u\n", ps, "", obj->flag_d); + PATHD_FORMAT("%*sflag_s: %u\n", ps, "", obj->flag_s); + PATHD_FORMAT("%*sflag_r: %u\n", ps, "", obj->flag_r); + PATHD_FORMAT("%*sflag_a: %u\n", ps, "", obj->flag_a); + PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c); +} + +void _format_pcep_object_lspa(int ps, struct pcep_object_lspa *obj) +{ + PATHD_FORMAT("%*slspa_exclude_any: 0x%08x\n", ps, "", + obj->lspa_exclude_any); + PATHD_FORMAT("%*slspa_include_any: 0x%08x\n", ps, "", + obj->lspa_include_any); + PATHD_FORMAT("%*slspa_include_all: 0x%08x\n", ps, "", + obj->lspa_include_all); + PATHD_FORMAT("%*ssetup_priority: %u\n", ps, "", obj->setup_priority); + PATHD_FORMAT("%*sholding_priority: %u\n", ps, "", + obj->holding_priority); + PATHD_FORMAT("%*sflag_local_protection: %u\n", ps, "", + obj->flag_local_protection); +} + +void _format_pcep_object_ipv4_endpoint(int ps, + struct pcep_object_endpoints_ipv4 *obj) +{ + PATHD_FORMAT("%*ssrc_ipv4: %pI4\n", ps, "", &obj->src_ipv4); + PATHD_FORMAT("%*sdst_ipv4: %pI4\n", ps, "", &obj->dst_ipv4); +} + +void _format_pcep_object_metric(int ps, struct pcep_object_metric *obj) +{ + PATHD_FORMAT("%*stype: %s (%u)\n", ps, "", + pcep_metric_type_name(obj->type), obj->type); + PATHD_FORMAT("%*sflag_b: %u\n", ps, "", obj->flag_b); + PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c); + PATHD_FORMAT("%*svalue: %f\n", ps, "", obj->value); +} + +void _format_pcep_object_bandwidth(int ps, struct pcep_object_bandwidth *obj) +{ + PATHD_FORMAT("%*sbandwidth: %f\n", ps, "", obj->bandwidth); +} + +void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj) +{ + PATHD_FORMAT("%*sni: %u\n", ps, "", obj->ni); + PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c); + PATHD_FORMAT("%*serr_code: %s (%u)\n", ps, "", + pcep_nopath_tlv_err_code_name(obj->err_code), + obj->err_code); +} + +void _format_pcep_object_objfun(int ps, + struct pcep_object_objective_function *obj) +{ + PATHD_FORMAT("%*sof_code: %s (%u)\n", ps, "", + objfun_type_name(obj->of_code), obj->of_code); +} + +void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj) +{ + double_linked_list *obj_list = obj->sub_objects; + double_linked_list_node *node; + struct pcep_object_ro_subobj *sub_obj; + + int ps2 = ps + DEBUG_IDENT_SIZE; + int i; + + if ((obj_list == NULL) || (obj_list->num_entries == 0)) { + PATHD_FORMAT("%*ssub_objects: []\n", ps, ""); + return; + } + + PATHD_FORMAT("%*ssub_objects:\n", ps, ""); + + for (node = obj_list->head, i = 0; node != NULL; + node = node->next_node, i++) { + sub_obj = (struct pcep_object_ro_subobj *)node->data; + PATHD_FORMAT("%*s- flag_subobj_loose_hop: %u\n", ps2 - 2, "", + sub_obj->flag_subobj_loose_hop); + PATHD_FORMAT("%*sro_subobj_type: %s (%u)\n", ps2, "", + pcep_ro_type_name(sub_obj->ro_subobj_type), + sub_obj->ro_subobj_type); + _format_pcep_object_ro_details(ps2, sub_obj); + } +} + +void _format_pcep_object_ro_details(int ps, struct pcep_object_ro_subobj *ro) +{ + switch (ro->ro_subobj_type) { + case RO_SUBOBJ_TYPE_IPV4: + _format_pcep_object_ro_ipv4(ps, + (struct pcep_ro_subobj_ipv4 *)ro); + break; + case RO_SUBOBJ_TYPE_SR: + _format_pcep_object_ro_sr(ps, (struct pcep_ro_subobj_sr *)ro); + break; + default: + PATHD_FORMAT("%*s...\n", ps, ""); + break; + } +} + +void _format_pcep_object_ro_ipv4(int ps, struct pcep_ro_subobj_ipv4 *obj) +{ + PATHD_FORMAT("%*sip_addr: %pI4\n", ps, "", &obj->ip_addr); + PATHD_FORMAT("%*sprefix_length: %u\n", ps, "", obj->prefix_length); + PATHD_FORMAT("%*sflag_local_protection: %u\n", ps, "", + obj->flag_local_protection); +} + +void _format_pcep_object_ro_sr(int ps, struct pcep_ro_subobj_sr *obj) +{ + PATHD_FORMAT("%*snai_type = %s (%u)\n", ps, "", + pcep_nai_type_name(obj->nai_type), obj->nai_type); + PATHD_FORMAT("%*sflag_f: %u\n", ps, "", obj->flag_f); + PATHD_FORMAT("%*sflag_s: %u\n", ps, "", obj->flag_s); + PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c); + PATHD_FORMAT("%*sflag_m: %u\n", ps, "", obj->flag_m); + + if (!obj->flag_s) { + PATHD_FORMAT("%*sSID: %u\n", ps, "", obj->sid); + if (obj->flag_m) { + PATHD_FORMAT("%*slabel: %u\n", ps, "", + GET_SR_ERO_SID_LABEL(obj->sid)); + if (obj->flag_c) { + PATHD_FORMAT("%*sTC: %u\n", ps, "", + GET_SR_ERO_SID_TC(obj->sid)); + PATHD_FORMAT("%*sS: %u\n", ps, "", + GET_SR_ERO_SID_S(obj->sid)); + PATHD_FORMAT("%*sTTL: %u\n", ps, "", + GET_SR_ERO_SID_TTL(obj->sid)); + } + } + } + + if (!obj->flag_f) { + struct in_addr *laddr4, *raddr4; + struct in6_addr *laddr6, *raddr6; + uint32_t *liface, *riface; + assert(obj->nai_list != NULL); + double_linked_list_node *n = obj->nai_list->head; + assert(n != NULL); + assert(n->data != NULL); + switch (obj->nai_type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + laddr4 = (struct in_addr *)n->data; + PATHD_FORMAT("%*sNAI: %pI4\n", ps, "", laddr4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + laddr6 = (struct in6_addr *)n->data; + PATHD_FORMAT("%*sNAI: %pI6\n", ps, "", laddr6); + break; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + assert(n->next_node != NULL); + assert(n->next_node->data != NULL); + laddr4 = (struct in_addr *)n->data; + raddr4 = (struct in_addr *)n->next_node->data; + PATHD_FORMAT("%*sNAI: %pI4/%pI4\n", ps, "", laddr4, + raddr4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + assert(n->next_node != NULL); + assert(n->next_node->data != NULL); + laddr6 = (struct in6_addr *)n->data; + raddr6 = (struct in6_addr *)n->next_node->data; + PATHD_FORMAT("%*sNAI: %pI6/%pI6\n", ps, "", laddr6, + raddr6); + break; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + laddr4 = (struct in_addr *)n->data; + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + liface = (uint32_t *)n->data; + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + raddr4 = (struct in_addr *)n->data; + assert(n != NULL); + assert(n->data != NULL); + riface = (uint32_t *)n->data; + PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "", + laddr4, *liface, raddr4, *riface); + break; + default: + PATHD_FORMAT("%*sNAI: UNSUPPORTED\n", ps, ""); + break; + } + } +} + +void _format_pcep_object_tlvs(int ps, struct pcep_object_header *obj) +{ + double_linked_list *tlv_list = obj->tlv_list; + struct pcep_object_tlv_header *tlv; + double_linked_list_node *node; + int ps2 = ps + DEBUG_IDENT_SIZE; + int i = 0; + + if (tlv_list == NULL) + return; + if (tlv_list->num_entries == 0) { + PATHD_FORMAT("%*stlvs: []\n", ps, ""); + return; + } + + PATHD_FORMAT("%*stlvs:\n", ps, ""); + + for (node = tlv_list->head, i = 0; node != NULL; + node = node->next_node, i++) { + tlv = (struct pcep_object_tlv_header *)node->data; + PATHD_FORMAT("%*s- ", ps2 - 2, ""); + _format_pcep_object_tlv(ps2, tlv); + } +} + +void _format_pcep_object_tlv(int ps, struct pcep_object_tlv_header *tlv_header) +{ + PATHD_FORMAT("type: %s (%u)\n", pcep_tlv_type_name(tlv_header->type), + tlv_header->type); + _format_pcep_object_tlv_details(ps, tlv_header); +} + +void _format_pcep_object_tlv_details(int ps, + struct pcep_object_tlv_header *tlv_header) +{ + switch (tlv_header->type) { + case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME: + _format_pcep_object_tlv_symbolic_path_name( + ps, (struct pcep_object_tlv_symbolic_path_name *) + tlv_header); + break; + case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY: + _format_pcep_object_tlv_stateful_pce_capability( + ps, (struct pcep_object_tlv_stateful_pce_capability *) + tlv_header); + break; + case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY: + _format_pcep_object_tlv_sr_pce_capability( + ps, + (struct pcep_object_tlv_sr_pce_capability *)tlv_header); + break; + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE: + _format_pcep_object_tlv_path_setup_type( + ps, + (struct pcep_object_tlv_path_setup_type *)tlv_header); + break; + default: + PATHD_FORMAT("%*s...\n", ps, ""); + break; + } +} + +void _format_pcep_object_tlv_symbolic_path_name( + int ps, struct pcep_object_tlv_symbolic_path_name *tlv) +{ + PATHD_FORMAT("%*ssymbolic_path_name: %.*s\n", ps, "", + tlv->symbolic_path_name_length, tlv->symbolic_path_name); +} + +void _format_pcep_object_tlv_stateful_pce_capability( + int ps, struct pcep_object_tlv_stateful_pce_capability *tlv) +{ + PATHD_FORMAT("%*sflag_u_lsp_update_capability: %u\n", ps, "", + tlv->flag_u_lsp_update_capability); + PATHD_FORMAT("%*sflag_s_include_db_version: %u\n", ps, "", + tlv->flag_s_include_db_version); + PATHD_FORMAT("%*sflag_i_lsp_instantiation_capability: %u\n", ps, "", + tlv->flag_i_lsp_instantiation_capability); + PATHD_FORMAT("%*sflag_t_triggered_resync: %u\n", ps, "", + tlv->flag_t_triggered_resync); + PATHD_FORMAT("%*sflag_d_delta_lsp_sync: %u\n", ps, "", + tlv->flag_d_delta_lsp_sync); + PATHD_FORMAT("%*sflag_f_triggered_initial_sync: %u\n", ps, "", + tlv->flag_f_triggered_initial_sync); +} + +void _format_pcep_object_tlv_sr_pce_capability( + int ps, struct pcep_object_tlv_sr_pce_capability *tlv) +{ + + PATHD_FORMAT("%*sflag_n: %u\n", ps, "", tlv->flag_n); + PATHD_FORMAT("%*sflag_x: %u\n", ps, "", tlv->flag_x); + PATHD_FORMAT("%*smax_sid_depth: %u\n", ps, "", tlv->max_sid_depth); +} + +void _format_pcep_object_tlv_path_setup_type( + int ps, struct pcep_object_tlv_path_setup_type *tlv) +{ + PATHD_FORMAT("%*spath_setup_type: %u\n", ps, "", tlv->path_setup_type); +} diff --git a/pathd/path_pcep_debug.h b/pathd/path_pcep_debug.h new file mode 100644 index 000000000..68b29ab65 --- /dev/null +++ b/pathd/path_pcep_debug.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_DEBUG_H_ +#define _PATH_PCEP_DEBUG_H_ + +#include "pathd/path_debug.h" +#include <pcep_pcc_api.h> +#include <pcep-objects.h> +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_pcc.h" +#include "pathd/path_pcep_lib.h" + +const char *pcc_status_name(enum pcc_status status); + +const char *pcep_error_type_name(enum pcep_error_type error_type); +const char *pcep_error_value_name(enum pcep_error_type error_type, + enum pcep_error_value error_value); +const char *pcep_event_type_name(pcep_event_type event_type); +const char *pcep_message_type_name(enum pcep_message_types pcep_message_type); +const char *pcep_object_class_name(enum pcep_object_classes obj_class); +const char *pcep_object_type_name(enum pcep_object_classes obj_class, + enum pcep_object_types obj_type); +const char *pcep_lsp_status_name(enum pcep_lsp_operational_status status); +const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type); +const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type); +const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type); +const char *pcep_metric_type_name(enum pcep_metric_types type); +const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes code); + +const char *format_objfun_set(uint32_t flags); +const char *format_pcc_opts(struct pcc_opts *ops); +const char *format_pcc_state(struct pcc_state *state); +const char *format_ctrl_state(struct ctrl_state *state); +const char *format_path(struct path *path); +const char *format_pcep_event(pcep_event *event); +const char *format_pcep_message(struct pcep_message *msg); +const char *format_yang_dnode(struct lyd_node *dnode); + +#endif // _PATH_PCEP_DEBUG_H_ diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c new file mode 100644 index 000000000..fc72be897 --- /dev/null +++ b/pathd/path_pcep_lib.c @@ -0,0 +1,1146 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <debug.h> +#include <pcep_utils_counters.h> +#include <pcep_timers.h> +#include "pathd/path_errors.h" +#include "pathd/path_memory.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_debug.h" +#include "pathd/path_pcep_memory.h" + +#define CLASS_TYPE(CLASS, TYPE) (((CLASS) << 16) | (TYPE)) +#define DEFAULT_LSAP_SETUP_PRIO 4 +#define DEFAULT_LSAP_HOLDING_PRIO 4 +#define DEFAULT_LSAP_LOCAL_PRETECTION false + +/* pceplib logging callback */ +static int pceplib_logging_cb(int level, const char *fmt, va_list args); + +/* Socket callbacks */ +static int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd, + void *payload); +static int pcep_lib_pceplib_socket_write_cb(void *fpt, void **thread, int fd, + void *payload); +static int pcep_lib_socket_read_ready(struct thread *thread); +static int pcep_lib_socket_write_ready(struct thread *thread); + +/* pceplib pcep_event callbacks */ +static void pcep_lib_pceplib_event_cb(void *fpt, pcep_event *event); + +/* pceplib pthread creation callback */ +static int pcep_lib_pthread_create_cb(pthread_t *pthread_id, + const pthread_attr_t *attr, + void *(*start_routine)(void *), + void *data, const char *thread_name); +void *pcep_lib_pthread_start_passthrough(void *data); +int pcep_lib_pthread_stop_cb(struct frr_pthread *, void **); + +/* Internal functions */ +static double_linked_list *pcep_lib_format_path(struct pcep_caps *caps, + struct path *path); +static void pcep_lib_format_constraints(struct path *path, + double_linked_list *objs); +static void pcep_lib_parse_open(struct pcep_caps *caps, + struct pcep_object_open *open); +static void +pcep_lib_parse_open_pce_capability(struct pcep_caps *caps, + struct pcep_object_tlv_header *tlv_header); +static void +pcep_lib_parse_open_objfun_list(struct pcep_caps *caps, + struct pcep_object_tlv_header *tlv_header); +static void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp); +static void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp); +static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp); +static void pcep_lib_parse_lspa(struct path *path, + struct pcep_object_lspa *lspa); +static void pcep_lib_parse_metric(struct path *path, + struct pcep_object_metric *obj); +static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero); +static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next, + struct pcep_ro_subobj_sr *sr); +static struct counters_group *copy_counter_group(struct counters_group *from); +static struct counters_subgroup * +copy_counter_subgroup(struct counters_subgroup *from); +static struct counter *copy_counter(struct counter *from); +static void free_counter_group(struct counters_group *group); +static void free_counter_subgroup(struct counters_subgroup *subgroup); +static void free_counter(struct counter *counter); + +struct pcep_lib_pthread_passthrough_data { + void *(*start_routine)(void *data); + void *data; +}; + +/* ------------ API Functions ------------ */ + +int pcep_lib_initialize(struct frr_pthread *fpt) +{ + PCEP_DEBUG("Initializing pceplib"); + + /* Register pceplib logging callback */ + register_logger(pceplib_logging_cb); + + /* Its ok that this object goes out of scope, as it + * wont be stored, and its values will be copied */ + struct pceplib_infra_config infra = { + /* Memory infrastructure */ + .pceplib_infra_mt = MTYPE_PCEPLIB_INFRA, + .pceplib_messages_mt = MTYPE_PCEPLIB_MESSAGES, + .malloc_func = (pceplib_malloc_func)qmalloc, + .calloc_func = (pceplib_calloc_func)qcalloc, + .realloc_func = (pceplib_realloc_func)qrealloc, + .strdup_func = (pceplib_strdup_func)qstrdup, + .free_func = (pceplib_free_func)qfree, + /* Timers infrastructure */ + .external_infra_data = fpt, + .socket_read_func = pcep_lib_pceplib_socket_read_cb, + .socket_write_func = pcep_lib_pceplib_socket_write_cb, + /* PCEP events */ + .pcep_event_func = pcep_lib_pceplib_event_cb, + /* PCEPlib pthread creation callback */ + .pthread_create_func = pcep_lib_pthread_create_cb}; + if (!initialize_pcc_infra(&infra)) { + flog_err(EC_PATH_PCEP_PCC_INIT, "failed to initialize pceplib"); + return 1; + } + + return 0; +} + +void pcep_lib_finalize(void) +{ + PCEP_DEBUG("Finalizing pceplib"); + if (!destroy_pcc()) { + flog_err(EC_PATH_PCEP_PCC_FINI, "failed to finalize pceplib"); + } +} + + +pcep_session * +pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr, + int dst_port, short msd, + const struct pcep_config_group_opts *pcep_options) +{ + pcep_configuration *config; + pcep_session *sess; + + config = create_default_pcep_configuration(); + config->dst_pcep_port = dst_port; + config->src_pcep_port = src_port; + if (IS_IPADDR_V6(src_addr)) { + config->is_src_ipv6 = true; + memcpy(&config->src_ip.src_ipv6, &src_addr->ipaddr_v6, + sizeof(struct in6_addr)); + } else { + config->is_src_ipv6 = false; + config->src_ip.src_ipv4 = src_addr->ipaddr_v4; + } + + config->support_stateful_pce_lsp_update = true; + config->support_pce_lsp_instantiation = false; + config->support_include_db_version = false; + config->support_lsp_triggered_resync = false; + config->support_lsp_delta_sync = false; + config->support_pce_triggered_initial_sync = false; + config->support_sr_te_pst = true; + config->pcc_can_resolve_nai_to_sid = false; + + config->max_sid_depth = msd; + config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = + pcep_options->draft07; + config->keep_alive_seconds = pcep_options->keep_alive_seconds; + config->min_keep_alive_seconds = pcep_options->min_keep_alive_seconds; + config->max_keep_alive_seconds = pcep_options->max_keep_alive_seconds; + config->dead_timer_seconds = pcep_options->dead_timer_seconds; + config->min_dead_timer_seconds = pcep_options->min_dead_timer_seconds; + config->max_dead_timer_seconds = pcep_options->max_dead_timer_seconds; + config->request_time_seconds = pcep_options->pcep_request_time_seconds; + /* TODO when available in the pceplib, set it here + pcep_options->state_timeout_inteval_seconds;*/ + + if (pcep_options->tcp_md5_auth != NULL + && pcep_options->tcp_md5_auth[0] != '\0') { + config->is_tcp_auth_md5 = true; + strncpy(config->tcp_authentication_str, + pcep_options->tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN); + } else { + config->is_tcp_auth_md5 = false; + } + + if (IS_IPADDR_V6(dst_addr)) { + sess = connect_pce_ipv6(config, &dst_addr->ipaddr_v6); + } else { + sess = connect_pce(config, &dst_addr->ipaddr_v4); + } + destroy_pcep_configuration(config); + return sess; +} + +void pcep_lib_disconnect(pcep_session *sess) +{ + assert(sess != NULL); + disconnect_pce(sess); +} + +/* Callback passed to pceplib to write to a socket. + * When the socket is ready to be written to, + * pcep_lib_socket_write_ready() will be called */ + +int pcep_lib_pceplib_socket_write_cb(void *fpt, void **thread, int fd, + void *payload) +{ + return pcep_thread_socket_write(fpt, thread, fd, payload, + pcep_lib_socket_write_ready); +} + +/* Callback passed to pceplib to read from a socket. + * When the socket is ready to be read from, + * pcep_lib_socket_read_ready() will be called */ + +int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd, + void *payload) +{ + return pcep_thread_socket_read(fpt, thread, fd, payload, + pcep_lib_socket_read_ready); +} + +/* Callbacks called by path_pcep_controller when a socket is ready to read/write + */ + +int pcep_lib_socket_write_ready(struct thread *thread) +{ + struct pcep_ctrl_socket_data *data = THREAD_ARG(thread); + assert(data != NULL); + + int retval = pceplib_external_socket_write(data->fd, data->payload); + XFREE(MTYPE_PCEP, data); + + return retval; +} + +int pcep_lib_socket_read_ready(struct thread *thread) +{ + struct pcep_ctrl_socket_data *data = THREAD_ARG(thread); + assert(data != NULL); + + int retval = pceplib_external_socket_read(data->fd, data->payload); + XFREE(MTYPE_PCEP, data); + + return retval; +} + +/* Callback passed to pceplib when a pcep_event is ready */ +void pcep_lib_pceplib_event_cb(void *fpt, pcep_event *event) +{ + pcep_thread_send_ctrl_event(fpt, event, pcep_thread_pcep_event); +} + +/* Wrapper function around the actual pceplib thread start function */ +void *pcep_lib_pthread_start_passthrough(void *data) +{ + struct frr_pthread *fpt = data; + struct pcep_lib_pthread_passthrough_data *passthrough_data = fpt->data; + void *start_routine_data = passthrough_data->data; + void *(*start_routine)(void *) = passthrough_data->start_routine; + XFREE(MTYPE_PCEP, passthrough_data); + + if (start_routine != NULL) { + return start_routine(start_routine_data); + } + + return NULL; +} + +int pcep_lib_pthread_create_cb(pthread_t *thread_id, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *data, + const char *thread_name) +{ + /* Since FRR calls the start_routine with a struct frr_pthread, + * we have to store the real data and callback in a passthrough + * and pass the actual data the start_routine needs */ + struct pcep_lib_pthread_passthrough_data *passthrough_data = XMALLOC( + MTYPE_PCEP, sizeof(struct pcep_lib_pthread_passthrough_data)); + passthrough_data->data = data; + passthrough_data->start_routine = start_routine; + + struct frr_pthread_attr fpt_attr = { + .start = pcep_lib_pthread_start_passthrough, + .stop = pcep_lib_pthread_stop_cb}; + struct frr_pthread *fpt = + frr_pthread_new(&fpt_attr, thread_name, "pcep"); + if (fpt == NULL) { + return 1; + } + + fpt->data = passthrough_data; + int retval = frr_pthread_run(fpt, attr); + if (retval) { + return retval; + } + + *thread_id = fpt->thread; + + return 0; +} + +int pcep_lib_pthread_stop_cb(struct frr_pthread *fpt, void **res) +{ + pcep_lib_finalize(); + frr_pthread_destroy(fpt); + + return 0; +} + +struct pcep_message *pcep_lib_format_report(struct pcep_caps *caps, + struct path *path) +{ + double_linked_list *objs = pcep_lib_format_path(caps, path); + return pcep_msg_create_report(objs); +} + +static struct pcep_object_rp *create_rp(uint32_t reqid) +{ + double_linked_list *rp_tlvs; + struct pcep_object_tlv_path_setup_type *setup_type_tlv; + struct pcep_object_rp *rp; + + rp_tlvs = dll_initialize(); + setup_type_tlv = pcep_tlv_create_path_setup_type(SR_TE_PST); + dll_append(rp_tlvs, setup_type_tlv); + + rp = pcep_obj_create_rp(0, false, false, false, true, reqid, rp_tlvs); + + return rp; +} + +struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps, + struct path *path) +{ + struct ipaddr *src = &path->pcc_addr; + struct ipaddr *dst = &path->nbkey.endpoint; + double_linked_list *objs; + struct pcep_object_rp *rp; + struct pcep_object_endpoints_ipv4 *endpoints_ipv4; + struct pcep_object_endpoints_ipv6 *endpoints_ipv6; + struct pcep_object_objective_function *of = NULL; + enum objfun_type objfun = OBJFUN_UNDEFINED; + + assert(src->ipa_type == dst->ipa_type); + + objs = dll_initialize(); + rp = create_rp(path->req_id); + rp->header.flag_p = true; + + pcep_lib_format_constraints(path, objs); + + /* Objective Function */ + if (path->has_pcc_objfun) { + objfun = path->pcc_objfun; + } + + if (objfun != OBJFUN_UNDEFINED) { + of = pcep_obj_create_objective_function(objfun, NULL); + assert(of != NULL); + of->header.flag_p = path->enforce_pcc_objfun; + dll_append(objs, of); + } + + if (IS_IPADDR_V6(src)) { + endpoints_ipv6 = pcep_obj_create_endpoint_ipv6(&src->ipaddr_v6, + &dst->ipaddr_v6); + endpoints_ipv6->header.flag_p = true; + return pcep_msg_create_request_ipv6(rp, endpoints_ipv6, objs); + } else { + endpoints_ipv4 = pcep_obj_create_endpoint_ipv4(&src->ipaddr_v4, + &dst->ipaddr_v4); + endpoints_ipv4->header.flag_p = true; + return pcep_msg_create_request(rp, endpoints_ipv4, objs); + } +} + +struct pcep_message *pcep_lib_format_error(int error_type, int error_value) +{ + return pcep_msg_create_error(error_type, error_value); +} + +struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid) +{ + struct pcep_object_notify *notify; + double_linked_list *objs; + struct pcep_object_rp *rp; + + notify = pcep_obj_create_notify( + PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED, + PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST); + objs = dll_initialize(); + rp = create_rp(reqid); + dll_append(objs, rp); + + return pcep_msg_create_notify(notify, objs); +} + +struct path *pcep_lib_parse_path(struct pcep_message *msg) +{ + struct path *path; + double_linked_list *objs = msg->obj_list; + double_linked_list_node *node; + + struct pcep_object_header *obj; + struct pcep_object_rp *rp = NULL; + struct pcep_object_srp *srp = NULL; + struct pcep_object_lsp *lsp = NULL; + struct pcep_object_lspa *lspa = NULL; + struct pcep_object_ro *ero = NULL; + struct pcep_object_metric *metric = NULL; + struct pcep_object_bandwidth *bandwidth = NULL; + struct pcep_object_objective_function *of = NULL; + + path = pcep_new_path(); + + for (node = objs->head; node != NULL; node = node->next_node) { + obj = (struct pcep_object_header *)node->data; + switch (CLASS_TYPE(obj->object_class, obj->object_type)) { + case CLASS_TYPE(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP): + assert(rp == NULL); + rp = (struct pcep_object_rp *)obj; + pcep_lib_parse_rp(path, rp); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP): + assert(srp == NULL); + srp = (struct pcep_object_srp *)obj; + pcep_lib_parse_srp(path, srp); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP): + /* Only support single LSP per message */ + assert(lsp == NULL); + lsp = (struct pcep_object_lsp *)obj; + pcep_lib_parse_lsp(path, lsp); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA): + assert(lspa == NULL); + lspa = (struct pcep_object_lspa *)obj; + pcep_lib_parse_lspa(path, lspa); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO): + /* Only support single ERO per message */ + assert(ero == NULL); + ero = (struct pcep_object_ro *)obj; + pcep_lib_parse_ero(path, ero); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC): + metric = (struct pcep_object_metric *)obj; + pcep_lib_parse_metric(path, metric); + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_BANDWIDTH, + PCEP_OBJ_TYPE_BANDWIDTH_REQ): + case CLASS_TYPE(PCEP_OBJ_CLASS_BANDWIDTH, + PCEP_OBJ_TYPE_BANDWIDTH_CISCO): + bandwidth = (struct pcep_object_bandwidth *)obj; + path->has_bandwidth = true; + path->bandwidth = bandwidth->bandwidth; + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH): + path->no_path = true; + break; + case CLASS_TYPE(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF): + of = (struct pcep_object_objective_function *)obj; + path->has_pce_objfun = true; + path->pce_objfun = of->of_code; + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, + "Unexpected PCEP object %s (%u) / %s (%u)", + pcep_object_class_name(obj->object_class), + obj->object_class, + pcep_object_type_name(obj->object_class, + obj->object_type), + obj->object_type); + break; + } + } + + return path; +} + +void pcep_lib_parse_capabilities(struct pcep_message *msg, + struct pcep_caps *caps) +{ + double_linked_list *objs = msg->obj_list; + double_linked_list_node *node; + + struct pcep_object_header *obj; + struct pcep_object_open *open = NULL; + + for (node = objs->head; node != NULL; node = node->next_node) { + obj = (struct pcep_object_header *)node->data; + switch (CLASS_TYPE(obj->object_class, obj->object_type)) { + case CLASS_TYPE(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN): + assert(open == NULL); + open = (struct pcep_object_open *)obj; + pcep_lib_parse_open(caps, open); + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT, + "Unexpected PCEP object %s (%u) / %s (%u)", + pcep_object_class_name(obj->object_class), + obj->object_class, + pcep_object_type_name(obj->object_class, + obj->object_type), + obj->object_type); + break; + } + } +} + +struct counters_group *pcep_lib_copy_counters(pcep_session *sess) +{ + if (!sess || !sess->pcep_session_counters) { + return NULL; + } + + return copy_counter_group(sess->pcep_session_counters); +} + +void pcep_lib_free_counters(struct counters_group *counters) +{ + free_counter_group(counters); +} + +pcep_session *pcep_lib_copy_pcep_session(pcep_session *sess) +{ + if (!sess) { + return NULL; + } + + pcep_session *copy; + copy = XCALLOC(MTYPE_PCEP, sizeof(*copy)); + memcpy(copy, sess, sizeof(*copy)); + /* These fields should not be accessed */ + copy->num_unknown_messages_time_queue = NULL; + copy->socket_comm_session = NULL; + copy->pcep_session_counters = NULL; + + return copy; +} + +/* ------------ pceplib logging callback ------------ */ + +int pceplib_logging_cb(int priority, const char *fmt, va_list args) +{ + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), fmt, args); + PCEP_DEBUG_PCEPLIB(priority, "pceplib: %s", buffer); + return 0; +} + +/* ------------ Internal Functions ------------ */ + +double_linked_list *pcep_lib_format_path(struct pcep_caps *caps, + struct path *path) +{ + struct in_addr addr_null; + double_linked_list *objs, *srp_tlvs, *lsp_tlvs, *ero_objs; + struct pcep_object_tlv_header *tlv; + struct pcep_object_ro_subobj *ero_obj; + struct pcep_object_srp *srp; + struct pcep_object_lsp *lsp; + struct pcep_object_ro *ero; + uint32_t encoded_binding_sid; + char binding_sid_lsp_tlv_data[6]; + + memset(&addr_null, 0, sizeof(addr_null)); + + objs = dll_initialize(); + + if (path->plsp_id != 0) { + /* SRP object */ + srp_tlvs = dll_initialize(); + tlv = (struct pcep_object_tlv_header *) + pcep_tlv_create_path_setup_type(SR_TE_PST); + assert(tlv != NULL); + dll_append(srp_tlvs, tlv); + srp = pcep_obj_create_srp(path->do_remove, path->srp_id, + srp_tlvs); + assert(srp != NULL); + srp->header.flag_p = true; + dll_append(objs, srp); + } + + /* LSP object */ + lsp_tlvs = dll_initialize(); + + if (path->plsp_id == 0 || IS_IPADDR_NONE(&path->nbkey.endpoint) + || IS_IPADDR_NONE(&path->pcc_addr)) { + tlv = (struct pcep_object_tlv_header *) + pcep_tlv_create_ipv4_lsp_identifiers( + &addr_null, &addr_null, 0, 0, &addr_null); + } else { + assert(path->pcc_addr.ipa_type + == path->nbkey.endpoint.ipa_type); + if (IS_IPADDR_V4(&path->pcc_addr)) { + tlv = (struct pcep_object_tlv_header *) + pcep_tlv_create_ipv4_lsp_identifiers( + &path->pcc_addr.ipaddr_v4, + &path->nbkey.endpoint.ipaddr_v4, 0, 0, + &path->pcc_addr.ipaddr_v4); + } else { + tlv = (struct pcep_object_tlv_header *) + pcep_tlv_create_ipv6_lsp_identifiers( + &path->pcc_addr.ipaddr_v6, + &path->nbkey.endpoint.ipaddr_v6, 0, 0, + &path->pcc_addr.ipaddr_v6); + } + } + assert(tlv != NULL); + dll_append(lsp_tlvs, tlv); + if (path->name != NULL) { + tlv = (struct pcep_object_tlv_header *) + /*FIXME: Remove the typecasty when pceplib is changed + to take a const char* */ + pcep_tlv_create_symbolic_path_name((char *)path->name, + strlen(path->name)); + assert(tlv != NULL); + dll_append(lsp_tlvs, tlv); + } + if ((path->plsp_id != 0) && (path->binding_sid != MPLS_LABEL_NONE)) { + memset(binding_sid_lsp_tlv_data, 0, 2); + encoded_binding_sid = htonl(path->binding_sid << 12); + memcpy(binding_sid_lsp_tlv_data + 2, &encoded_binding_sid, 4); + tlv = (struct pcep_object_tlv_header *) + pcep_tlv_create_tlv_arbitrary( + binding_sid_lsp_tlv_data, + sizeof(binding_sid_lsp_tlv_data), 65505); + assert(tlv != NULL); + dll_append(lsp_tlvs, tlv); + } + lsp = pcep_obj_create_lsp( + path->plsp_id, path->status, path->was_created /* C Flag */, + path->go_active /* A Flag */, path->was_removed /* R Flag */, + path->is_synching /* S Flag */, path->is_delegated /* D Flag */, + lsp_tlvs); + assert(lsp != NULL); + lsp->header.flag_p = true; + dll_append(objs, lsp); + /* ERO object */ + ero_objs = dll_initialize(); + for (struct path_hop *hop = path->first_hop; hop != NULL; + hop = hop->next) { + uint32_t sid; + + /* Only supporting MPLS hops with both sid and nai */ + assert(hop->is_mpls); + assert(hop->has_sid); + + if (hop->has_attribs) { + sid = ENCODE_SR_ERO_SID(hop->sid.mpls.label, + hop->sid.mpls.traffic_class, + hop->sid.mpls.is_bottom, + hop->sid.mpls.ttl); + } else { + sid = ENCODE_SR_ERO_SID(hop->sid.mpls.label, 0, 0, 0); + } + + ero_obj = NULL; + if (hop->has_nai) { + assert(hop->nai.type != PCEP_SR_SUBOBJ_NAI_ABSENT); + assert(hop->nai.type + != PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY); + assert(hop->nai.type != PCEP_SR_SUBOBJ_NAI_UNKNOWN); + switch (hop->nai.type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_ipv4_node( + hop->is_loose, !hop->has_sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls, /* M Flag */ + sid, + &hop->nai.local_addr.ipaddr_v4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_ipv6_node( + hop->is_loose, !hop->has_sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls, /* M Flag */ + sid, + &hop->nai.local_addr.ipaddr_v6); + break; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_ipv4_adj( + hop->is_loose, !hop->has_sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls, /* M Flag */ + sid, + &hop->nai.local_addr.ipaddr_v4, + &hop->nai.remote_addr + .ipaddr_v4); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_ipv6_adj( + hop->is_loose, !hop->has_sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls, /* M Flag */ + sid, + &hop->nai.local_addr.ipaddr_v6, + &hop->nai.remote_addr + .ipaddr_v6); + break; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj( + hop->is_loose, !hop->has_sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls, /* M Flag */ + sid, + hop->nai.local_addr.ipaddr_v4 + .s_addr, + hop->nai.local_iface, + hop->nai.remote_addr.ipaddr_v4 + .s_addr, + hop->nai.remote_iface); + break; + default: + break; + } + } + if (ero_obj == NULL) { + ero_obj = (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_sr_nonai( + hop->is_loose, sid, + hop->has_attribs, /* C Flag */ + hop->is_mpls); /* M Flag */ + } + dll_append(ero_objs, ero_obj); + } + ero = pcep_obj_create_ero(ero_objs); + assert(ero != NULL); + ero->header.flag_p = true; + dll_append(objs, ero); + + if (path->plsp_id == 0) { + return objs; + } + + pcep_lib_format_constraints(path, objs); + + return objs; +} + +void pcep_lib_format_constraints(struct path *path, double_linked_list *objs) +{ + struct pcep_object_metric *metric; + struct pcep_object_bandwidth *bandwidth; + struct pcep_object_lspa *lspa; + + /* LSPA object */ + if (path->has_affinity_filters) { + lspa = pcep_obj_create_lspa( + path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1], + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1], + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1], + DEFAULT_LSAP_SETUP_PRIO, DEFAULT_LSAP_HOLDING_PRIO, + DEFAULT_LSAP_LOCAL_PRETECTION); + assert(lspa != NULL); + lspa->header.flag_p = true; + dll_append(objs, lspa); + } + + /* Bandwidth Objects */ + if (path->has_bandwidth) { + /* Requested Bandwidth */ + bandwidth = pcep_obj_create_bandwidth(path->bandwidth); + assert(bandwidth != NULL); + bandwidth->header.flag_p = path->enforce_bandwidth; + dll_append(objs, bandwidth); + } + + /* Metric Objects */ + for (struct path_metric *m = path->first_metric; m != NULL; + m = m->next) { + metric = pcep_obj_create_metric(m->type, m->is_bound, + m->is_computed, m->value); + assert(metric != NULL); + metric->header.flag_p = m->enforce; + dll_append(objs, metric); + } +} + +void pcep_lib_parse_open(struct pcep_caps *caps, struct pcep_object_open *open) +{ + double_linked_list *tlvs = open->header.tlv_list; + double_linked_list_node *node; + struct pcep_object_tlv_header *tlv_header; + + caps->is_stateful = false; + caps->supported_ofs_are_known = false; + caps->supported_ofs = 0; + + for (node = tlvs->head; node != NULL; node = node->next_node) { + tlv_header = (struct pcep_object_tlv_header *)node->data; + switch (tlv_header->type) { + case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY: + pcep_lib_parse_open_pce_capability(caps, tlv_header); + break; + case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY: + break; + case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST: + pcep_lib_parse_open_objfun_list(caps, tlv_header); + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected OPEN's TLV %s (%u)", + pcep_tlv_type_name(tlv_header->type), + tlv_header->type); + break; + } + } +} + +void pcep_lib_parse_open_pce_capability( + struct pcep_caps *caps, struct pcep_object_tlv_header *tlv_header) +{ + struct pcep_object_tlv_stateful_pce_capability *tlv; + tlv = (struct pcep_object_tlv_stateful_pce_capability *)tlv_header; + caps->is_stateful = tlv->flag_u_lsp_update_capability; +} + +void pcep_lib_parse_open_objfun_list(struct pcep_caps *caps, + struct pcep_object_tlv_header *tlv_header) +{ + double_linked_list_node *node; + struct pcep_object_tlv_of_list *tlv; + tlv = (struct pcep_object_tlv_of_list *)tlv_header; + uint16_t of_code; + caps->supported_ofs_are_known = true; + for (node = tlv->of_list->head; node != NULL; node = node->next_node) { + of_code = *(uint16_t *)node->data; + if (of_code >= 32) { + zlog_warn( + "Ignoring unexpected objective function with code %u", + of_code); + continue; + } + SET_FLAG(caps->supported_ofs, of_code); + } +} + +void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp) +{ + double_linked_list *tlvs = rp->header.tlv_list; + double_linked_list_node *node; + struct pcep_object_tlv_header *tlv; + + /* We ignore the other flags and priority for now */ + path->req_id = rp->request_id; + path->has_pce_objfun = false; + path->pce_objfun = OBJFUN_UNDEFINED; + + for (node = tlvs->head; node != NULL; node = node->next_node) { + tlv = (struct pcep_object_tlv_header *)node->data; + switch (tlv->type) { + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE: + // TODO: enforce the path setup type is SR_TE_PST + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected RP's TLV %s (%u)", + pcep_tlv_type_name(tlv->type), tlv->type); + break; + } + } +} + +void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp) +{ + double_linked_list *tlvs = srp->header.tlv_list; + double_linked_list_node *node; + struct pcep_object_tlv_header *tlv; + + path->do_remove = srp->flag_lsp_remove; + path->srp_id = srp->srp_id_number; + + for (node = tlvs->head; node != NULL; node = node->next_node) { + tlv = (struct pcep_object_tlv_header *)node->data; + switch (tlv->type) { + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE: + // TODO: enforce the path setup type is SR_TE_PST + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected SRP's TLV %s (%u)", + pcep_tlv_type_name(tlv->type), tlv->type); + break; + } + } +} + +void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp) +{ + double_linked_list *tlvs = lsp->header.tlv_list; + double_linked_list_node *node; + struct pcep_object_tlv_header *tlv; + + path->plsp_id = lsp->plsp_id; + path->status = lsp->operational_status; + path->go_active = lsp->flag_a; + path->was_created = lsp->flag_c; + path->was_removed = lsp->flag_r; + path->is_synching = lsp->flag_s; + path->is_delegated = lsp->flag_d; + + if (tlvs == NULL) + return; + + for (node = tlvs->head; node != NULL; node = node->next_node) { + tlv = (struct pcep_object_tlv_header *)node->data; + switch (tlv->type) { + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV, + "Unexpected LSP TLV %s (%u)", + pcep_tlv_type_name(tlv->type), tlv->type); + break; + } + } +} + +void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa) +{ + path->has_affinity_filters = true; + path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1] = + lspa->lspa_exclude_any; + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1] = + lspa->lspa_include_any; + path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1] = + lspa->lspa_include_all; +} + +void pcep_lib_parse_metric(struct path *path, struct pcep_object_metric *obj) +{ + struct path_metric *metric; + + metric = pcep_new_metric(); + metric->type = obj->type; + metric->is_bound = obj->flag_b; + metric->is_computed = obj->flag_c; + metric->value = obj->value; + metric->next = path->first_metric; + path->first_metric = metric; +} + +void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero) +{ + struct path_hop *hop = NULL; + double_linked_list *objs = ero->sub_objects; + double_linked_list_node *node; + struct pcep_object_ro_subobj *obj; + + for (node = objs->tail; node != NULL; node = node->prev_node) { + obj = (struct pcep_object_ro_subobj *)node->data; + switch (obj->ro_subobj_type) { + case RO_SUBOBJ_TYPE_SR: + hop = pcep_lib_parse_ero_sr( + hop, (struct pcep_ro_subobj_sr *)obj); + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ, + "Unexpected ERO sub-object %s (%u)", + pcep_ro_type_name(obj->ro_subobj_type), + obj->ro_subobj_type); + break; + } + } + + path->first_hop = hop; +} + +struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next, + struct pcep_ro_subobj_sr *sr) +{ + struct path_hop *hop = NULL; + union sid sid; + + /* Only support IPv4 node with SID */ + assert(!sr->flag_s); + + if (sr->flag_m) { + sid.mpls = (struct sid_mpls){ + .label = GET_SR_ERO_SID_LABEL(sr->sid), + .traffic_class = GET_SR_ERO_SID_TC(sr->sid), + .is_bottom = GET_SR_ERO_SID_S(sr->sid), + .ttl = GET_SR_ERO_SID_TTL(sr->sid)}; + } else { + sid.value = sr->sid; + } + + hop = pcep_new_hop(); + *hop = (struct path_hop){.next = next, + .is_loose = + sr->ro_subobj.flag_subobj_loose_hop, + .has_sid = !sr->flag_s, + .is_mpls = sr->flag_m, + .has_attribs = sr->flag_c, + .sid = sid, + .has_nai = !sr->flag_f, + .nai = {.type = sr->nai_type}}; + + if (!sr->flag_f) { + assert(sr->nai_list != NULL); + double_linked_list_node *n = sr->nai_list->head; + assert(n != NULL); + assert(n->data != NULL); + switch (sr->nai_type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + hop->nai.local_addr.ipa_type = IPADDR_V4; + memcpy(&hop->nai.local_addr.ipaddr_v4, n->data, + sizeof(struct in_addr)); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + hop->nai.local_addr.ipa_type = IPADDR_V6; + memcpy(&hop->nai.local_addr.ipaddr_v6, n->data, + sizeof(struct in6_addr)); + break; + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + hop->nai.local_addr.ipa_type = IPADDR_V4; + memcpy(&hop->nai.local_addr.ipaddr_v4, n->data, + sizeof(struct in_addr)); + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + hop->nai.remote_addr.ipa_type = IPADDR_V4; + memcpy(&hop->nai.remote_addr.ipaddr_v4, n->data, + sizeof(struct in_addr)); + break; + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + hop->nai.local_addr.ipa_type = IPADDR_V6; + memcpy(&hop->nai.local_addr.ipaddr_v6, n->data, + sizeof(struct in6_addr)); + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + hop->nai.remote_addr.ipa_type = IPADDR_V6; + memcpy(&hop->nai.remote_addr.ipaddr_v6, n->data, + sizeof(struct in6_addr)); + break; + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + hop->nai.local_addr.ipa_type = IPADDR_V4; + memcpy(&hop->nai.local_addr.ipaddr_v4, n->data, + sizeof(struct in_addr)); + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + hop->nai.local_iface = *(uint32_t *)n->data; + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + hop->nai.remote_addr.ipa_type = IPADDR_V4; + memcpy(&hop->nai.remote_addr.ipaddr_v4, n->data, + sizeof(struct in_addr)); + n = n->next_node; + assert(n != NULL); + assert(n->data != NULL); + hop->nai.remote_iface = *(uint32_t *)n->data; + break; + default: + hop->has_nai = false; + flog_warn(EC_PATH_PCEP_UNEXPECTED_SR_NAI, + "Unexpected SR segment NAI type %s (%u)", + pcep_nai_type_name(sr->nai_type), + sr->nai_type); + break; + } + } + + return hop; +} + +struct counters_group *copy_counter_group(struct counters_group *from) +{ + int size, i; + struct counters_group *result; + if (from == NULL) + return NULL; + assert(from->max_subgroups >= from->num_subgroups); + result = XCALLOC(MTYPE_PCEP, sizeof(*result)); + memcpy(result, from, sizeof(*result)); + size = sizeof(struct counters_subgroup *) * (from->max_subgroups + 1); + result->subgroups = XCALLOC(MTYPE_PCEP, size); + for (i = 0; i <= from->max_subgroups; i++) + result->subgroups[i] = + copy_counter_subgroup(from->subgroups[i]); + return result; +} + +struct counters_subgroup *copy_counter_subgroup(struct counters_subgroup *from) +{ + int size, i; + struct counters_subgroup *result; + if (from == NULL) + return NULL; + assert(from->max_counters >= from->num_counters); + result = XCALLOC(MTYPE_PCEP, sizeof(*result)); + memcpy(result, from, sizeof(*result)); + size = sizeof(struct counter *) * (from->max_counters + 1); + result->counters = XCALLOC(MTYPE_PCEP, size); + for (i = 0; i <= from->max_counters; i++) + result->counters[i] = copy_counter(from->counters[i]); + return result; +} + +struct counter *copy_counter(struct counter *from) +{ + struct counter *result; + if (from == NULL) + return NULL; + result = XCALLOC(MTYPE_PCEP, sizeof(*result)); + memcpy(result, from, sizeof(*result)); + return result; +} + +void free_counter_group(struct counters_group *group) +{ + int i; + if (group == NULL) + return; + for (i = 0; i <= group->max_subgroups; i++) + free_counter_subgroup(group->subgroups[i]); + XFREE(MTYPE_PCEP, group->subgroups); + XFREE(MTYPE_PCEP, group); +} + +void free_counter_subgroup(struct counters_subgroup *subgroup) +{ + int i; + if (subgroup == NULL) + return; + for (i = 0; i <= subgroup->max_counters; i++) + free_counter(subgroup->counters[i]); + XFREE(MTYPE_PCEP, subgroup->counters); + XFREE(MTYPE_PCEP, subgroup); +} + +void free_counter(struct counter *counter) +{ + if (counter == NULL) + return; + XFREE(MTYPE_PCEP, counter); +} diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h new file mode 100644 index 000000000..3bea28432 --- /dev/null +++ b/pathd/path_pcep_lib.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_LIB_H_ +#define _PATH_PCEP_LIB_H_ + +#include <stdbool.h> +#include <pcep_pcc_api.h> +#include "frr_pthread.h" +#include "pathd/path_pcep.h" + +int pcep_lib_initialize(struct frr_pthread *fpt); +void pcep_lib_finalize(void); +pcep_session * +pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr, + int dst_port, short msd, + const struct pcep_config_group_opts *pcep_options); +void pcep_lib_disconnect(pcep_session *sess); +struct pcep_message *pcep_lib_format_report(struct pcep_caps *caps, + struct path *path); +struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps, + struct path *path); +struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid); + +struct pcep_message *pcep_lib_format_error(int error_type, int error_value); +struct path *pcep_lib_parse_path(struct pcep_message *msg); +void pcep_lib_parse_capabilities(struct pcep_message *msg, + struct pcep_caps *caps); +struct counters_group *pcep_lib_copy_counters(pcep_session *sess); +void pcep_lib_free_counters(struct counters_group *counters); +pcep_session *pcep_lib_copy_pcep_session(pcep_session *sess); + +#endif // _PATH_PCEP_LIB_H_ diff --git a/pathd/path_pcep_memory.c b/pathd/path_pcep_memory.c new file mode 100644 index 000000000..8f608090a --- /dev/null +++ b/pathd/path_pcep_memory.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include <memory.h> + +#include "pathd/path_pcep_memory.h" + +DEFINE_MTYPE(PATHD, PCEP, "PCEP module") +DEFINE_MTYPE(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure") +DEFINE_MTYPE(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages") diff --git a/pathd/path_pcep_memory.h b/pathd/path_pcep_memory.h new file mode 100644 index 000000000..05c5e2d82 --- /dev/null +++ b/pathd/path_pcep_memory.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_PATH_PCEP_MEMORY_H_ +#define _FRR_PATH_PCEP_MEMORY_H_ + +#include "pathd/path_memory.h" + +DECLARE_MTYPE(PCEP) +DECLARE_MTYPE(PCEPLIB_INFRA) +DECLARE_MTYPE(PCEPLIB_MESSAGES) + +#endif /* _FRR_PATH_PCEP_MEMORY_H_ */ diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c new file mode 100644 index 000000000..6bb5ce4bd --- /dev/null +++ b/pathd/path_pcep_pcc.c @@ -0,0 +1,1817 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* TODOS AND KNOWN ISSUES: + - Delete mapping from NB keys to PLSPID when an LSP is deleted either + by the PCE or by NB. + - Revert the hacks to work around ODL requiring a report with + operational status DOWN when an LSP is activated. + - Enforce only the PCE a policy has been delegated to can update it. + - If the router-id is used because the PCC IP is not specified + (either IPv4 or IPv6), the connection to the PCE is not reset + when the router-id changes. +*/ + +#include <zebra.h> + +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" + +#include "pathd/pathd.h" +#include "pathd/path_zebra.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep_memory.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_config.h" +#include "pathd/path_pcep_debug.h" + + +/* The number of time we will skip connecting if we are missing the PCC + * address for an inet family different from the selected transport one*/ +#define OTHER_FAMILY_MAX_RETRIES 4 +#define MAX_ERROR_MSG_SIZE 256 +#define MAX_COMPREQ_TRIES 3 + + +/* PCEP Event Handler */ +static void handle_pcep_open(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg); +static void handle_pcep_message(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg); +static void handle_pcep_lsp_update(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg); +static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg); +static void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg); + +/* Internal Functions */ +static const char *ipaddr_type_name(struct ipaddr *addr); +static bool filter_path(struct pcc_state *pcc_state, struct path *path); +static void select_pcc_addresses(struct pcc_state *pcc_state); +static void select_transport_address(struct pcc_state *pcc_state); +static void update_tag(struct pcc_state *pcc_state); +static void update_originator(struct pcc_state *pcc_state); +static void schedule_reconnect(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +static void schedule_session_timeout(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +static void cancel_session_timeout(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +static void send_pcep_message(struct pcc_state *pcc_state, + struct pcep_message *msg); +static void send_pcep_error(struct pcc_state *pcc_state, + enum pcep_error_type error_type, + enum pcep_error_value error_value); +static void send_report(struct pcc_state *pcc_state, struct path *path); +static void send_comp_request(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct req_entry *req); +static void cancel_comp_requests(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +static void cancel_comp_request(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct req_entry *req); +static void specialize_outgoing_path(struct pcc_state *pcc_state, + struct path *path); +static void specialize_incoming_path(struct pcc_state *pcc_state, + struct path *path); +static bool validate_incoming_path(struct pcc_state *pcc_state, + struct path *path, char *errbuff, + size_t buffsize); +static void set_pcc_address(struct pcc_state *pcc_state, + struct lsp_nb_key *nbkey, struct ipaddr *addr); +static int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs); +static int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs); +static int get_previous_best_pce(struct pcc_state **pcc); +static int get_best_pce(struct pcc_state **pcc); +static int get_pce_count_connected(struct pcc_state **pcc); +static bool update_best_pce(struct pcc_state **pcc, int best); + +/* Data Structure Helper Functions */ +static void lookup_plspid(struct pcc_state *pcc_state, struct path *path); +static void lookup_nbkey(struct pcc_state *pcc_state, struct path *path); +static void free_req_entry(struct req_entry *req); +static struct req_entry *push_new_req(struct pcc_state *pcc_state, + struct path *path); +static void repush_req(struct pcc_state *pcc_state, struct req_entry *req); +static struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid); +static bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path); +static void remove_reqid_mapping(struct pcc_state *pcc_state, + struct path *path); +static uint32_t lookup_reqid(struct pcc_state *pcc_state, struct path *path); +static bool has_pending_req_for(struct pcc_state *pcc_state, struct path *path); + +/* Data Structure Callbacks */ +static int plspid_map_cmp(const struct plspid_map_data *a, + const struct plspid_map_data *b); +static uint32_t plspid_map_hash(const struct plspid_map_data *e); +static int nbkey_map_cmp(const struct nbkey_map_data *a, + const struct nbkey_map_data *b); +static uint32_t nbkey_map_hash(const struct nbkey_map_data *e); +static int req_map_cmp(const struct req_map_data *a, + const struct req_map_data *b); +static uint32_t req_map_hash(const struct req_map_data *e); + +/* Data Structure Declarations */ +DECLARE_HASH(plspid_map, struct plspid_map_data, mi, plspid_map_cmp, + plspid_map_hash) +DECLARE_HASH(nbkey_map, struct nbkey_map_data, mi, nbkey_map_cmp, + nbkey_map_hash) +DECLARE_HASH(req_map, struct req_map_data, mi, req_map_cmp, req_map_hash) + +static inline int req_entry_compare(const struct req_entry *a, + const struct req_entry *b) +{ + return a->path->req_id - b->path->req_id; +} +RB_GENERATE(req_entry_head, req_entry, entry, req_entry_compare) + + +/* ------------ API Functions ------------ */ + +struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state, int index) +{ + struct pcc_state *pcc_state = XCALLOC(MTYPE_PCEP, sizeof(*pcc_state)); + + pcc_state->id = index; + pcc_state->status = PCEP_PCC_DISCONNECTED; + pcc_state->next_reqid = 1; + pcc_state->next_plspid = 1; + + RB_INIT(req_entry_head, &pcc_state->requests); + + update_tag(pcc_state); + update_originator(pcc_state); + + PCEP_DEBUG("%s PCC initialized", pcc_state->tag); + + return pcc_state; +} + +void pcep_pcc_finalize(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + PCEP_DEBUG("%s PCC finalizing...", pcc_state->tag); + + pcep_pcc_disable(ctrl_state, pcc_state); + + if (pcc_state->pcc_opts != NULL) { + XFREE(MTYPE_PCEP, pcc_state->pcc_opts); + pcc_state->pcc_opts = NULL; + } + if (pcc_state->pce_opts != NULL) { + XFREE(MTYPE_PCEP, pcc_state->pce_opts); + pcc_state->pce_opts = NULL; + } + if (pcc_state->originator != NULL) { + XFREE(MTYPE_PCEP, pcc_state->originator); + pcc_state->originator = NULL; + } + + if (pcc_state->t_reconnect != NULL) { + thread_cancel(&pcc_state->t_reconnect); + pcc_state->t_reconnect = NULL; + } + + if (pcc_state->t_update_best != NULL) { + thread_cancel(&pcc_state->t_update_best); + pcc_state->t_update_best = NULL; + } + + if (pcc_state->t_session_timeout != NULL) { + thread_cancel(&pcc_state->t_session_timeout); + pcc_state->t_session_timeout = NULL; + } + + XFREE(MTYPE_PCEP, pcc_state); +} + +int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs) +{ + int retval; + + if (lhs == NULL) { + return 1; + } + + if (rhs == NULL) { + return -1; + } + + retval = lhs->port - rhs->port; + if (retval != 0) { + return retval; + } + + retval = lhs->msd - rhs->msd; + if (retval != 0) { + return retval; + } + + if (IS_IPADDR_V4(&lhs->addr)) { + retval = memcmp(&lhs->addr.ipaddr_v4, &rhs->addr.ipaddr_v4, + sizeof(lhs->addr.ipaddr_v4)); + if (retval != 0) { + return retval; + } + } else if (IS_IPADDR_V6(&lhs->addr)) { + retval = memcmp(&lhs->addr.ipaddr_v6, &rhs->addr.ipaddr_v6, + sizeof(lhs->addr.ipaddr_v6)); + if (retval != 0) { + return retval; + } + } + + return 0; +} + +int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs) +{ + if (lhs == NULL) { + return 1; + } + + if (rhs == NULL) { + return -1; + } + + int retval = lhs->port - rhs->port; + if (retval != 0) { + return retval; + } + + retval = strcmp(lhs->pce_name, rhs->pce_name); + if (retval != 0) { + return retval; + } + + retval = lhs->precedence - rhs->precedence; + if (retval != 0) { + return retval; + } + + retval = memcmp(&lhs->addr, &rhs->addr, sizeof(lhs->addr)); + if (retval != 0) { + return retval; + } + + return 0; +} + +int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, + struct pcc_opts *pcc_opts, struct pce_opts *pce_opts) +{ + int ret = 0; + + // If the options did not change, then there is nothing to do + if ((compare_pce_opts(pce_opts, pcc_state->pce_opts) == 0) + && (compare_pcc_opts(pcc_opts, pcc_state->pcc_opts) == 0)) { + return ret; + } + + if ((ret = pcep_pcc_disable(ctrl_state, pcc_state))) { + XFREE(MTYPE_PCEP, pcc_opts); + XFREE(MTYPE_PCEP, pce_opts); + return ret; + } + + if (pcc_state->pcc_opts != NULL) { + XFREE(MTYPE_PCEP, pcc_state->pcc_opts); + } + if (pcc_state->pce_opts != NULL) { + XFREE(MTYPE_PCEP, pcc_state->pce_opts); + } + + pcc_state->pcc_opts = pcc_opts; + pcc_state->pce_opts = pce_opts; + + if (IS_IPADDR_V4(&pcc_opts->addr)) { + pcc_state->pcc_addr_v4 = pcc_opts->addr.ipaddr_v4; + SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4); + } else { + UNSET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4); + } + + if (IS_IPADDR_V6(&pcc_opts->addr)) { + memcpy(&pcc_state->pcc_addr_v6, &pcc_opts->addr.ipaddr_v6, + sizeof(struct in6_addr)); + SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6); + } else { + UNSET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6); + } + + update_tag(pcc_state); + update_originator(pcc_state); + + return pcep_pcc_enable(ctrl_state, pcc_state); +} + +void pcep_pcc_reconnect(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + if (pcc_state->status == PCEP_PCC_DISCONNECTED) + pcep_pcc_enable(ctrl_state, pcc_state); +} + +int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) +{ + char pcc_buff[40]; + char pce_buff[40]; + + assert(pcc_state->status == PCEP_PCC_DISCONNECTED); + assert(pcc_state->sess == NULL); + + if (pcc_state->t_reconnect != NULL) { + thread_cancel(&pcc_state->t_reconnect); + pcc_state->t_reconnect = NULL; + } + + select_transport_address(pcc_state); + + /* Even though we are connecting using IPv6. we want to have an IPv4 + * address so we can handle candidate path with IPv4 endpoints */ + if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) { + if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) { + flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + "skipping connection to PCE %s:%d due to " + "missing PCC IPv4 address", + ipaddr2str(&pcc_state->pce_opts->addr, + pce_buff, sizeof(pce_buff)), + pcc_state->pce_opts->port); + schedule_reconnect(ctrl_state, pcc_state); + return 0; + } else { + flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + "missing IPv4 PCC address, IPv4 candidate " + "paths will be ignored"); + } + } + + /* Even though we are connecting using IPv4. we want to have an IPv6 + * address so we can handle candidate path with IPv6 endpoints */ + if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) { + if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) { + flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + "skipping connection to PCE %s:%d due to " + "missing PCC IPv6 address", + ipaddr2str(&pcc_state->pce_opts->addr, + pce_buff, sizeof(pce_buff)), + pcc_state->pce_opts->port); + schedule_reconnect(ctrl_state, pcc_state); + return 0; + } else { + flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + "missing IPv6 PCC address, IPv6 candidate " + "paths will be ignored"); + } + } + + /* Even if the maximum retries to try to have all the familly addresses + * have been spent, we still need the one for the transport familly */ + if (pcc_state->pcc_addr_tr.ipa_type == IPADDR_NONE) { + flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, + "skipping connection to PCE %s:%d due to missing " + "PCC address", + ipaddr2str(&pcc_state->pce_opts->addr, pce_buff, + sizeof(pce_buff)), + pcc_state->pce_opts->port); + schedule_reconnect(ctrl_state, pcc_state); + return 0; + } + + PCEP_DEBUG("%s PCC connecting", pcc_state->tag); + pcc_state->sess = pcep_lib_connect( + &pcc_state->pcc_addr_tr, pcc_state->pcc_opts->port, + &pcc_state->pce_opts->addr, pcc_state->pce_opts->port, + pcc_state->pcc_opts->msd, &pcc_state->pce_opts->config_opts); + + if (pcc_state->sess == NULL) { + flog_warn(EC_PATH_PCEP_LIB_CONNECT, + "failed to connect to PCE %s:%d from %s:%d", + ipaddr2str(&pcc_state->pce_opts->addr, pce_buff, + sizeof(pce_buff)), + pcc_state->pce_opts->port, + ipaddr2str(&pcc_state->pcc_addr_tr, pcc_buff, + sizeof(pcc_buff)), + pcc_state->pcc_opts->port); + schedule_reconnect(ctrl_state, pcc_state); + return 0; + } + + // In case some best pce alternative were waiting to activate + if (pcc_state->t_update_best != NULL) { + thread_cancel(&pcc_state->t_update_best); + pcc_state->t_update_best = NULL; + } + + pcc_state->status = PCEP_PCC_CONNECTING; + + return 0; +} + +int pcep_pcc_disable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) +{ + switch (pcc_state->status) { + case PCEP_PCC_DISCONNECTED: + return 0; + case PCEP_PCC_CONNECTING: + case PCEP_PCC_SYNCHRONIZING: + case PCEP_PCC_OPERATING: + PCEP_DEBUG("%s Disconnecting PCC...", pcc_state->tag); + cancel_comp_requests(ctrl_state, pcc_state); + pcep_lib_disconnect(pcc_state->sess); + /* No need to remove if any PCEs is connected */ + if (get_pce_count_connected(ctrl_state->pcc) == 0) { + pcep_thread_remove_candidate_path_segments(ctrl_state, + pcc_state); + } + pcc_state->sess = NULL; + pcc_state->status = PCEP_PCC_DISCONNECTED; + return 0; + default: + return 1; + } +} + +void pcep_pcc_sync_path(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct path *path) +{ + if (pcc_state->status == PCEP_PCC_SYNCHRONIZING) { + path->is_synching = true; + } else if (pcc_state->status == PCEP_PCC_OPERATING) + path->is_synching = false; + else + return; + + path->go_active = true; + + /* Accumulate the dynamic paths without any LSP so computation + * requests can be performed after synchronization */ + if ((path->type == SRTE_CANDIDATE_TYPE_DYNAMIC) + && (path->first_hop == NULL) + && !has_pending_req_for(pcc_state, path)) { + PCEP_DEBUG("%s Scheduling computation request for path %s", + pcc_state->tag, path->name); + push_new_req(pcc_state, path); + return; + } + + /* Synchronize the path if the PCE supports LSP updates and the + * endpoint address familly is supported */ + if (pcc_state->caps.is_stateful) { + if (filter_path(pcc_state, path)) { + PCEP_DEBUG("%s Synchronizing path %s", pcc_state->tag, + path->name); + send_report(pcc_state, path); + } else { + PCEP_DEBUG( + "%s Skipping %s candidate path %s " + "synchronization", + pcc_state->tag, + ipaddr_type_name(&path->nbkey.endpoint), + path->name); + } + } +} + +void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + struct req_entry *req; + + if (pcc_state->status != PCEP_PCC_SYNCHRONIZING + && pcc_state->status != PCEP_PCC_OPERATING) + return; + + if (pcc_state->caps.is_stateful + && pcc_state->status == PCEP_PCC_SYNCHRONIZING) { + struct path *path = pcep_new_path(); + *path = (struct path){.name = NULL, + .srp_id = 0, + .plsp_id = 0, + .status = PCEP_LSP_OPERATIONAL_DOWN, + .do_remove = false, + .go_active = false, + .was_created = false, + .was_removed = false, + .is_synching = false, + .is_delegated = false, + .first_hop = NULL, + .first_metric = NULL}; + send_report(pcc_state, path); + pcep_free_path(path); + } + + pcc_state->synchronized = true; + pcc_state->status = PCEP_PCC_OPERATING; + + PCEP_DEBUG("%s Synchronization done", pcc_state->tag); + + /* Start the computation request accumulated during synchronization */ + RB_FOREACH (req, req_entry_head, &pcc_state->requests) { + send_comp_request(ctrl_state, pcc_state, req); + } +} + +void pcep_pcc_send_report(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct path *path) +{ + if (pcc_state->status != PCEP_PCC_OPERATING) + return; + + if (pcc_state->caps.is_stateful) { + PCEP_DEBUG("%s Send report for candidate path %s", + pcc_state->tag, path->name); + send_report(pcc_state, path); + } +} + +/* ------------ Timeout handler ------------ */ + +void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + enum pcep_ctrl_timer_type type, void *param) +{ + struct req_entry *req; + + switch (type) { + case TO_COMPUTATION_REQUEST: + assert(param != NULL); + req = (struct req_entry *)param; + pop_req(pcc_state, req->path->req_id); + flog_warn(EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT, + "Computation request %d timeout", req->path->req_id); + cancel_comp_request(ctrl_state, pcc_state, req); + if (req->retry_count++ < MAX_COMPREQ_TRIES) { + repush_req(pcc_state, req); + send_comp_request(ctrl_state, pcc_state, req); + return; + } + if (pcc_state->caps.is_stateful) { + struct path *path; + PCEP_DEBUG( + "%s Delegating undefined dynamic path %s to PCE %s", + pcc_state->tag, req->path->name, + pcc_state->originator); + path = pcep_copy_path(req->path); + path->is_delegated = true; + send_report(pcc_state, path); + free_req_entry(req); + } + break; + default: + break; + } +} + + +/* ------------ Pathd event handler ------------ */ + +void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + enum pcep_pathd_event_type type, + struct path *path) +{ + struct req_entry *req; + + if (pcc_state->status != PCEP_PCC_OPERATING) + return; + + /* Skipping candidate path with endpoint that do not match the + * configured or deduced PCC IP version */ + if (!filter_path(pcc_state, path)) { + PCEP_DEBUG("%s Skipping %s candidate path %s event", + pcc_state->tag, + ipaddr_type_name(&path->nbkey.endpoint), path->name); + return; + } + + switch (type) { + case PCEP_PATH_CREATED: + if (has_pending_req_for(pcc_state, path)) { + PCEP_DEBUG( + "%s Candidate path %s created, computation request already sent", + pcc_state->tag, path->name); + return; + } + PCEP_DEBUG("%s Candidate path %s created", pcc_state->tag, + path->name); + if ((path->first_hop == NULL) + && (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC)) { + req = push_new_req(pcc_state, path); + send_comp_request(ctrl_state, pcc_state, req); + } else if (pcc_state->caps.is_stateful) + send_report(pcc_state, path); + return; + case PCEP_PATH_UPDATED: + PCEP_DEBUG("%s Candidate path %s updated", pcc_state->tag, + path->name); + if (pcc_state->caps.is_stateful) + send_report(pcc_state, path); + return; + case PCEP_PATH_REMOVED: + PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag, + path->name); + path->was_removed = true; + if (pcc_state->caps.is_stateful) + send_report(pcc_state, path); + return; + default: + flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR, + "Unexpected pathd event received by pcc %s: %u", + pcc_state->tag, type); + return; + } +} + + +/* ------------ PCEP event handler ------------ */ + +void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, pcep_event *event) +{ + PCEP_DEBUG("%s Received PCEP event: %s", pcc_state->tag, + pcep_event_type_name(event->event_type)); + switch (event->event_type) { + case PCC_CONNECTED_TO_PCE: + assert(PCEP_PCC_CONNECTING == pcc_state->status); + PCEP_DEBUG("%s Connection established", pcc_state->tag); + pcc_state->status = PCEP_PCC_SYNCHRONIZING; + pcc_state->retry_count = 0; + pcc_state->synchronized = false; + PCEP_DEBUG("%s Starting PCE synchronization", pcc_state->tag); + cancel_session_timeout(ctrl_state, pcc_state); + pcep_pcc_calculate_best_pce(ctrl_state->pcc); + pcep_thread_start_sync(ctrl_state, pcc_state->id); + break; + case PCC_SENT_INVALID_OPEN: + PCEP_DEBUG("%s Sent invalid OPEN message", pcc_state->tag); + PCEP_DEBUG( + "%s Reconciling values: keep alive (%d) dead timer (%d) seconds ", + pcc_state->tag, + pcc_state->sess->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + pcc_state->sess->pcc_config + .dead_timer_pce_negotiated_seconds); + pcc_state->pce_opts->config_opts.keep_alive_seconds = + pcc_state->sess->pcc_config + .keep_alive_pce_negotiated_timer_seconds; + pcc_state->pce_opts->config_opts.dead_timer_seconds = + pcc_state->sess->pcc_config + .dead_timer_pce_negotiated_seconds; + break; + + case PCC_RCVD_INVALID_OPEN: + PCEP_DEBUG("%s Received invalid OPEN message", pcc_state->tag); + PCEP_DEBUG_PCEP("%s PCEP message: %s", pcc_state->tag, + format_pcep_message(event->message)); + break; + case PCE_DEAD_TIMER_EXPIRED: + case PCE_CLOSED_SOCKET: + case PCE_SENT_PCEP_CLOSE: + case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED: + case PCC_PCEP_SESSION_CLOSED: + case PCC_RCVD_MAX_INVALID_MSGS: + case PCC_RCVD_MAX_UNKOWN_MSGS: + pcep_pcc_disable(ctrl_state, pcc_state); + schedule_reconnect(ctrl_state, pcc_state); + schedule_session_timeout(ctrl_state, pcc_state); + break; + case MESSAGE_RECEIVED: + PCEP_DEBUG_PCEP("%s Received PCEP message: %s", pcc_state->tag, + format_pcep_message(event->message)); + if (pcc_state->status == PCEP_PCC_CONNECTING) { + if (event->message->msg_header->type == PCEP_TYPE_OPEN) + handle_pcep_open(ctrl_state, pcc_state, + event->message); + break; + } + assert(pcc_state->status == PCEP_PCC_SYNCHRONIZING + || pcc_state->status == PCEP_PCC_OPERATING); + handle_pcep_message(ctrl_state, pcc_state, event->message); + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT, + "Unexpected event from pceplib: %s", + format_pcep_event(event)); + break; + } +} + + +/*------------------ Multi-PCE --------------------- */ + +/* Internal util function, returns true if sync is necessary, false otherwise */ +bool update_best_pce(struct pcc_state **pcc, int best) +{ + PCEP_DEBUG(" recalculating pce precedence "); + if (best) { + struct pcc_state *best_pcc_state = + pcep_pcc_get_pcc_by_id(pcc, best); + if (best_pcc_state->previous_best != best_pcc_state->is_best) { + PCEP_DEBUG(" %s Resynch best (%i) previous best (%i)", + best_pcc_state->tag, best_pcc_state->id, + best_pcc_state->previous_best); + return true; + } else { + PCEP_DEBUG( + " %s No Resynch best (%i) previous best (%i)", + best_pcc_state->tag, best_pcc_state->id, + best_pcc_state->previous_best); + } + } else { + PCEP_DEBUG(" No best pce available, all pce seem disconnected"); + } + + return false; +} + +int get_best_pce(struct pcc_state **pcc) +{ + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts) { + if (pcc[i]->is_best == true) { + return pcc[i]->id; + } + } + } + return 0; +} + +int get_pce_count_connected(struct pcc_state **pcc) +{ + int count = 0; + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts + && pcc[i]->status != PCEP_PCC_DISCONNECTED) { + count++; + } + } + return count; +} + +int get_previous_best_pce(struct pcc_state **pcc) +{ + int previous_best_pce = -1; + + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts && pcc[i]->previous_best == true + && pcc[i]->status != PCEP_PCC_DISCONNECTED) { + previous_best_pce = i; + break; + } + } + return previous_best_pce != -1 ? pcc[previous_best_pce]->id : 0; +} + +/* Called by path_pcep_controller EV_REMOVE_PCC + * Event handler when a PCC is removed. */ +int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state, + struct pcc_state **pcc) +{ + int new_best_pcc_id = -1; + new_best_pcc_id = pcep_pcc_calculate_best_pce(pcc); + if (new_best_pcc_id) { + if (update_best_pce(ctrl_state->pcc, new_best_pcc_id) == true) { + pcep_thread_start_sync(ctrl_state, new_best_pcc_id); + } + } + + return 0; +} + +/* Called by path_pcep_controller EV_SYNC_PATH + * Event handler when a path is sync'd. */ +int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id, + struct pcc_state **pcc) +{ + int previous_best_pcc_id = -1; + + if (pcc_id == get_best_pce(pcc)) { + previous_best_pcc_id = get_previous_best_pce(pcc); + if (previous_best_pcc_id != 0) { + /* while adding new pce, path has to resync to the + * previous best. pcep_thread_start_sync() will be + * called by the calling function */ + if (update_best_pce(ctrl_state->pcc, + previous_best_pcc_id) + == true) { + cancel_comp_requests( + ctrl_state, + pcep_pcc_get_pcc_by_id( + pcc, previous_best_pcc_id)); + pcep_thread_start_sync(ctrl_state, + previous_best_pcc_id); + } + } + } + + return 0; +} + +/* Called by path_pcep_controller when the TM_CALCULATE_BEST_PCE + * timer expires */ +int pcep_pcc_timer_update_best_pce(struct ctrl_state *ctrl_state, int pcc_id) +{ + int ret = 0; + /* resync whatever was the new best */ + int prev_best = get_best_pce(ctrl_state->pcc); + int best_id = pcep_pcc_calculate_best_pce(ctrl_state->pcc); + if (best_id && prev_best != best_id) { // Avoid Multiple call + struct pcc_state *pcc_state = + pcep_pcc_get_pcc_by_id(ctrl_state->pcc, best_id); + if (update_best_pce(ctrl_state->pcc, pcc_state->id) == true) { + pcep_thread_start_sync(ctrl_state, pcc_state->id); + } + } + + return ret; +} + +/* Called by path_pcep_controller::pcep_thread_event_update_pce_options() + * Returns the best PCE id */ +int pcep_pcc_calculate_best_pce(struct pcc_state **pcc) +{ + int best_precedence = 255; // DEFAULT_PCE_PRECEDENCE; + int best_pce = -1; + int one_connected_pce = -1; + int previous_best_pce = -1; + int step_0_best = -1; + int step_0_previous = -1; + int pcc_count = 0; + + // Get state + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts) { + zlog_debug( + "multi-pce: calculate all : i (%i) is_best (%i) previous_best (%i) ", + i, pcc[i]->is_best, pcc[i]->previous_best); + pcc_count++; + + if (pcc[i]->is_best == true) { + step_0_best = i; + } + if (pcc[i]->previous_best == true) { + step_0_previous = i; + } + } + } + + if (!pcc_count) { + return 0; + } + + // Calculate best + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts + && pcc[i]->status != PCEP_PCC_DISCONNECTED) { + one_connected_pce = i; // In case none better + if (pcc[i]->pce_opts->precedence <= best_precedence) { + if (best_pce != -1 + && pcc[best_pce]->pce_opts->precedence + == pcc[i]->pce_opts + ->precedence) { + if (ipaddr_cmp( + &pcc[i]->pce_opts->addr, + &pcc[best_pce] + ->pce_opts->addr) + > 0) + // collide of precedences so + // compare ip + best_pce = i; + } else { + if (!pcc[i]->previous_best) { + best_precedence = + pcc[i]->pce_opts + ->precedence; + best_pce = i; + } + } + } + } + } + + zlog_debug( + "multi-pce: calculate data : sb (%i) sp (%i) oc (%i) b (%i) ", + step_0_best, step_0_previous, one_connected_pce, best_pce); + + // Changed of state so ... + if (step_0_best != best_pce) { + // Calculate previous + previous_best_pce = step_0_best; + // Clean state + if (step_0_best != -1) { + pcc[step_0_best]->is_best = false; + } + if (step_0_previous != -1) { + pcc[step_0_previous]->previous_best = false; + } + + // Set previous + if (previous_best_pce != -1 + && pcc[previous_best_pce]->status + == PCEP_PCC_DISCONNECTED) { + pcc[previous_best_pce]->previous_best = true; + zlog_debug("multi-pce: previous best pce (%i) ", + previous_best_pce + 1); + } + + + // Set best + if (best_pce != -1) { + pcc[best_pce]->is_best = true; + zlog_debug("multi-pce: best pce (%i) ", best_pce + 1); + } else { + if (one_connected_pce != -1) { + best_pce = one_connected_pce; + pcc[one_connected_pce]->is_best = true; + zlog_debug( + "multi-pce: one connected best pce (default) (%i) ", + one_connected_pce + 1); + } else { + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] && pcc[i]->pce_opts) { + best_pce = i; + pcc[i]->is_best = true; + zlog_debug( + "(disconnected) best pce (default) (%i) ", + i + 1); + break; + } + } + } + } + } + + return ((best_pce == -1) ? 0 : pcc[best_pce]->id); +} + +int pcep_pcc_get_pcc_id_by_ip_port(struct pcc_state **pcc, + struct pce_opts *pce_opts) +{ + if (pcc == NULL) { + return 0; + } + + for (int idx = 0; idx < MAX_PCC; idx++) { + if (pcc[idx]) { + if ((ipaddr_cmp((const struct ipaddr *)&pcc[idx] + ->pce_opts->addr, + (const struct ipaddr *)&pce_opts->addr) + == 0) + && pcc[idx]->pce_opts->port == pce_opts->port) { + zlog_debug("found pcc_id (%d) idx (%d)", + pcc[idx]->id, idx); + return pcc[idx]->id; + } + } + } + return 0; +} + +int pcep_pcc_get_pcc_id_by_idx(struct pcc_state **pcc, int idx) +{ + if (pcc == NULL || idx < 0) { + return 0; + } + + return pcc[idx] ? pcc[idx]->id : 0; +} + +struct pcc_state *pcep_pcc_get_pcc_by_id(struct pcc_state **pcc, int id) +{ + if (pcc == NULL || id < 0) { + return NULL; + } + + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i]) { + if (pcc[i]->id == id) { + zlog_debug("found id (%d) pcc_idx (%d)", + pcc[i]->id, i); + return pcc[i]; + } + } + } + + return NULL; +} + +struct pcc_state *pcep_pcc_get_pcc_by_name(struct pcc_state **pcc, + const char *pce_name) +{ + if (pcc == NULL || pce_name == NULL) { + return NULL; + } + + for (int i = 0; i < MAX_PCC; i++) { + if (pcc[i] == NULL) { + continue; + } + + if (strcmp(pcc[i]->pce_opts->pce_name, pce_name) == 0) { + return pcc[i]; + } + } + + return NULL; +} + +int pcep_pcc_get_pcc_idx_by_id(struct pcc_state **pcc, int id) +{ + if (pcc == NULL) { + return -1; + } + + for (int idx = 0; idx < MAX_PCC; idx++) { + if (pcc[idx]) { + if (pcc[idx]->id == id) { + zlog_debug("found pcc_id (%d) array_idx (%d)", + pcc[idx]->id, idx); + return idx; + } + } + } + + return -1; +} + +int pcep_pcc_get_free_pcc_idx(struct pcc_state **pcc) +{ + assert(pcc != NULL); + + for (int idx = 0; idx < MAX_PCC; idx++) { + if (pcc[idx] == NULL) { + zlog_debug("new pcc_idx (%d)", idx); + return idx; + } + } + + return -1; +} + +int pcep_pcc_get_pcc_id(struct pcc_state *pcc) +{ + return ((pcc == NULL) ? 0 : pcc->id); +} + +void pcep_pcc_copy_pcc_info(struct pcc_state **pcc, + struct pcep_pcc_info *pcc_info) +{ + struct pcc_state *pcc_state = + pcep_pcc_get_pcc_by_name(pcc, pcc_info->pce_name); + if (!pcc_state) { + return; + } + + pcc_info->ctrl_state = NULL; + pcc_info->msd = pcc_state->pcc_opts->msd; + pcc_info->pcc_port = pcc_state->pcc_opts->port; + pcc_info->next_plspid = pcc_state->next_plspid; + pcc_info->next_reqid = pcc_state->next_reqid; + pcc_info->status = pcc_state->status; + pcc_info->pcc_id = pcc_state->id; + pcc_info->is_best_multi_pce = pcc_state->is_best; + pcc_info->precedence = + pcc_state->pce_opts ? pcc_state->pce_opts->precedence : 0; + memcpy(&pcc_info->pcc_addr, &pcc_state->pcc_addr_tr, + sizeof(struct ipaddr)); +} + + +/*------------------ PCEP Message handlers --------------------- */ + +void handle_pcep_open(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct pcep_message *msg) +{ + assert(msg->msg_header->type == PCEP_TYPE_OPEN); + pcep_lib_parse_capabilities(msg, &pcc_state->caps); + PCEP_DEBUG("PCE capabilities: %s, %s%s", + pcc_state->caps.is_stateful ? "stateful" : "stateless", + pcc_state->caps.supported_ofs_are_known + ? (pcc_state->caps.supported_ofs == 0 + ? "no objective functions supported" + : "supported objective functions are ") + : "supported objective functions are unknown", + format_objfun_set(pcc_state->caps.supported_ofs)); +} + +void handle_pcep_message(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct pcep_message *msg) +{ + if (pcc_state->status != PCEP_PCC_OPERATING) + return; + + switch (msg->msg_header->type) { + case PCEP_TYPE_INITIATE: + handle_pcep_lsp_initiate(ctrl_state, pcc_state, msg); + break; + case PCEP_TYPE_UPDATE: + handle_pcep_lsp_update(ctrl_state, pcc_state, msg); + break; + case PCEP_TYPE_PCREP: + handle_pcep_comp_reply(ctrl_state, pcc_state, msg); + break; + default: + flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE, + "Unexpected pcep message from pceplib: %s", + format_pcep_message(msg)); + break; + } +} + +void handle_pcep_lsp_update(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg) +{ + char err[MAX_ERROR_MSG_SIZE] = ""; + struct path *path; + path = pcep_lib_parse_path(msg); + lookup_nbkey(pcc_state, path); + /* TODO: Investigate if this is safe to do in the controller thread */ + path_pcep_config_lookup(path); + specialize_incoming_path(pcc_state, path); + PCEP_DEBUG("%s Received LSP update", pcc_state->tag); + PCEP_DEBUG_PATH("%s", format_path(path)); + + if (validate_incoming_path(pcc_state, path, err, sizeof(err))) + pcep_thread_update_path(ctrl_state, pcc_state->id, path); + else { + /* FIXME: Monitor the amount of errors from the PCE and + * possibly disconnect and blacklist */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Unsupported PCEP protocol feature: %s", err); + pcep_free_path(path); + } +} + +void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg) +{ + PCEP_DEBUG("%s Received LSP initiate, not supported yet", + pcc_state->tag); + + /* TODO when we support both PCC and PCE initiated sessions, + * we should first check the session type before + * rejecting this message. */ + send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION, + PCEP_ERRV_LSP_NOT_PCE_INITIATED); +} + +void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + struct pcep_message *msg) +{ + char err[MAX_ERROR_MSG_SIZE] = ""; + struct req_entry *req; + struct path *path; + + path = pcep_lib_parse_path(msg); + req = pop_req(pcc_state, path->req_id); + if (req == NULL) { + /* TODO: check the rate of bad computation reply and close + * the connection if more that a given rate. + */ + PCEP_DEBUG( + "%s Received computation reply for unknown request " + "%d", + pcc_state->tag, path->req_id); + PCEP_DEBUG_PATH("%s", format_path(path)); + send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF, + PCEP_ERRV_UNASSIGNED); + return; + } + + /* Cancel the computation request timeout */ + pcep_thread_cancel_timer(&req->t_retry); + + /* Transfer relevent metadata from the request to the response */ + path->nbkey = req->path->nbkey; + path->plsp_id = req->path->plsp_id; + path->type = req->path->type; + path->name = XSTRDUP(MTYPE_PCEP, req->path->name); + specialize_incoming_path(pcc_state, path); + + PCEP_DEBUG("%s Received computation reply %d (no-path: %s)", + pcc_state->tag, path->req_id, + path->no_path ? "true" : "false"); + PCEP_DEBUG_PATH("%s", format_path(path)); + + if (path->no_path) { + PCEP_DEBUG("%s Computation for path %s did not find any result", + pcc_state->tag, path->name); + } else if (validate_incoming_path(pcc_state, path, err, sizeof(err))) { + /* Updating a dynamic path will automatically delegate it */ + pcep_thread_update_path(ctrl_state, pcc_state->id, path); + free_req_entry(req); + return; + } else { + /* FIXME: Monitor the amount of errors from the PCE and + * possibly disconnect and blacklist */ + flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE, + "Unsupported PCEP protocol feature: %s", err); + } + + pcep_free_path(path); + + /* Delegate the path regardless of the outcome */ + /* TODO: For now we are using the path from the request, when + * pathd API is thread safe, we could get a new path */ + if (pcc_state->caps.is_stateful) { + PCEP_DEBUG("%s Delegating undefined dynamic path %s to PCE %s", + pcc_state->tag, path->name, pcc_state->originator); + path = pcep_copy_path(req->path); + path->is_delegated = true; + send_report(pcc_state, path); + pcep_free_path(path); + } + + free_req_entry(req); +} + + +/* ------------ Internal Functions ------------ */ + +const char *ipaddr_type_name(struct ipaddr *addr) +{ + if (IS_IPADDR_V4(addr)) + return "IPv4"; + if (IS_IPADDR_V6(addr)) + return "IPv6"; + return "undefined"; +} + +bool filter_path(struct pcc_state *pcc_state, struct path *path) +{ + return (IS_IPADDR_V4(&path->nbkey.endpoint) + && CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) + || (IS_IPADDR_V6(&path->nbkey.endpoint) + && CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)); +} + +void select_pcc_addresses(struct pcc_state *pcc_state) +{ + /* If no IPv4 address was specified, try to get one from zebra */ + if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) { + if (get_ipv4_router_id(&pcc_state->pcc_addr_v4)) { + SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4); + } + } + + /* If no IPv6 address was specified, try to get one from zebra */ + if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) { + if (get_ipv6_router_id(&pcc_state->pcc_addr_v6)) { + SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6); + } + } +} + +void select_transport_address(struct pcc_state *pcc_state) +{ + struct ipaddr *taddr = &pcc_state->pcc_addr_tr; + + select_pcc_addresses(pcc_state); + + taddr->ipa_type = IPADDR_NONE; + + /* Select a transport source address in function of the configured PCE + * address */ + if (IS_IPADDR_V4(&pcc_state->pce_opts->addr)) { + if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) { + taddr->ipa_type = IPADDR_V4; + taddr->ipaddr_v4 = pcc_state->pcc_addr_v4; + } + } else { + if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) { + taddr->ipa_type = IPADDR_V6; + taddr->ipaddr_v6 = pcc_state->pcc_addr_v6; + } + } +} + +void update_tag(struct pcc_state *pcc_state) +{ + if (pcc_state->pce_opts != NULL) { + assert(!IS_IPADDR_NONE(&pcc_state->pce_opts->addr)); + if (IS_IPADDR_V6(&pcc_state->pce_opts->addr)) { + snprintfrr(pcc_state->tag, sizeof(pcc_state->tag), + "%pI6:%i (%u)", + &pcc_state->pce_opts->addr.ipaddr_v6, + pcc_state->pce_opts->port, pcc_state->id); + } else { + snprintfrr(pcc_state->tag, sizeof(pcc_state->tag), + "%pI4:%i (%u)", + &pcc_state->pce_opts->addr.ipaddr_v4, + pcc_state->pce_opts->port, pcc_state->id); + } + } else { + snprintfrr(pcc_state->tag, sizeof(pcc_state->tag), "(%u)", + pcc_state->id); + } +} + +void update_originator(struct pcc_state *pcc_state) +{ + char *originator; + if (pcc_state->originator != NULL) { + XFREE(MTYPE_PCEP, pcc_state->originator); + pcc_state->originator = NULL; + } + if (pcc_state->pce_opts == NULL) + return; + originator = XCALLOC(MTYPE_PCEP, 52); + assert(!IS_IPADDR_NONE(&pcc_state->pce_opts->addr)); + if (IS_IPADDR_V6(&pcc_state->pce_opts->addr)) { + snprintfrr(originator, 52, "%pI6:%i", + &pcc_state->pce_opts->addr.ipaddr_v6, + pcc_state->pce_opts->port); + } else { + snprintfrr(originator, 52, "%pI4:%i", + &pcc_state->pce_opts->addr.ipaddr_v4, + pcc_state->pce_opts->port); + } + pcc_state->originator = originator; +} + +void schedule_reconnect(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + pcc_state->retry_count++; + pcep_thread_schedule_reconnect(ctrl_state, pcc_state->id, + pcc_state->retry_count, + &pcc_state->t_reconnect); + if (pcc_state->retry_count == 1) { + pcep_thread_schedule_sync_best_pce( + ctrl_state, pcc_state->id, + pcc_state->pce_opts->config_opts + .delegation_timeout_seconds, + &pcc_state->t_update_best); + } +} + +void schedule_session_timeout(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + /* No need to schedule timeout if multiple PCEs are connected */ + if (get_pce_count_connected(ctrl_state->pcc)) { + PCEP_DEBUG_PCEP( + "schedule_session_timeout not setting timer for multi-pce mode"); + + return; + } + + pcep_thread_schedule_session_timeout( + ctrl_state, pcep_pcc_get_pcc_id(pcc_state), + pcc_state->pce_opts->config_opts + .session_timeout_inteval_seconds, + &pcc_state->t_session_timeout); +} + +void cancel_session_timeout(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + /* No need to schedule timeout if multiple PCEs are connected */ + if (pcc_state->t_session_timeout == NULL) { + PCEP_DEBUG_PCEP("cancel_session_timeout timer thread NULL"); + return; + } + + PCEP_DEBUG_PCEP("Cancel session_timeout timer"); + pcep_thread_cancel_timer(&pcc_state->t_session_timeout); + pcc_state->t_session_timeout = NULL; +} + +void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg) +{ + if (pcc_state->sess != NULL) { + PCEP_DEBUG_PCEP("%s Sending PCEP message: %s", pcc_state->tag, + format_pcep_message(msg)); + send_message(pcc_state->sess, msg, true); + } +} + +void send_pcep_error(struct pcc_state *pcc_state, + enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + struct pcep_message *msg; + PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)", + pcc_state->tag, pcep_error_type_name(error_type), error_type, + pcep_error_value_name(error_type, error_value), error_value); + msg = pcep_lib_format_error(error_type, error_value); + send_pcep_message(pcc_state, msg); +} + +void send_report(struct pcc_state *pcc_state, struct path *path) +{ + struct pcep_message *report; + + path->req_id = 0; + specialize_outgoing_path(pcc_state, path); + PCEP_DEBUG_PATH("%s Sending path %s: %s", pcc_state->tag, path->name, + format_path(path)); + report = pcep_lib_format_report(&pcc_state->caps, path); + send_pcep_message(pcc_state, report); +} + +/* Updates the path for the PCE, updating the delegation and creation flags */ +void specialize_outgoing_path(struct pcc_state *pcc_state, struct path *path) +{ + bool is_delegated = false; + bool was_created = false; + + lookup_plspid(pcc_state, path); + + set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); + path->sender = pcc_state->pcc_addr_tr; + + /* TODO: When the pathd API have a way to mark a path as + * delegated, use it instead of considering all dynamic path + * delegated. We need to disable the originator check for now, + * because path could be delegated without having any originator yet */ + // if ((path->originator == NULL) + // || (strcmp(path->originator, pcc_state->originator) == 0)) { + // is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC) + // && (path->first_hop != NULL); + // /* it seems the PCE consider updating an LSP a creation ?!? + // at least Cisco does... */ + // was_created = path->update_origin == SRTE_ORIGIN_PCEP; + // } + is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC); + was_created = path->update_origin == SRTE_ORIGIN_PCEP; + + path->pcc_id = pcc_state->id; + path->go_active = is_delegated && pcc_state->is_best; + path->is_delegated = is_delegated && pcc_state->is_best; + path->was_created = was_created; +} + +/* Updates the path for the PCC */ +void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path) +{ + set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr); + path->sender = pcc_state->pce_opts->addr; + path->pcc_id = pcc_state->id; + path->update_origin = SRTE_ORIGIN_PCEP; + path->originator = XSTRDUP(MTYPE_PCEP, pcc_state->originator); +} + +/* Ensure the path can be handled by the PCC and if not, sends an error */ +bool validate_incoming_path(struct pcc_state *pcc_state, struct path *path, + char *errbuff, size_t buffsize) +{ + struct path_hop *hop; + enum pcep_error_type err_type = 0; + enum pcep_error_value err_value = PCEP_ERRV_UNASSIGNED; + + for (hop = path->first_hop; hop != NULL; hop = hop->next) { + /* Hops without SID are not supported */ + if (!hop->has_sid) { + snprintfrr(errbuff, buffsize, "SR segment without SID"); + err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; + err_value = PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING; + break; + } + /* Hops with non-MPLS SID are not supported */ + if (!hop->is_mpls) { + snprintfrr(errbuff, buffsize, + "SR segment with non-MPLS SID"); + err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; + err_value = PCEP_ERRV_UNSUPPORTED_NAI; + break; + } + } + + if (err_type != 0) { + send_pcep_error(pcc_state, err_type, err_value); + return false; + } + + return true; +} + +void send_comp_request(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct req_entry *req) +{ + assert(req != NULL); + + if (req->t_retry) + return; + + assert(req->path != NULL); + assert(req->path->req_id > 0); + assert(RB_FIND(req_entry_head, &pcc_state->requests, req) == req); + assert(lookup_reqid(pcc_state, req->path) == req->path->req_id); + + int timeout; + char buff[40]; + struct pcep_message *msg; + + if (!pcc_state->is_best) { + return; + } + /* TODO: Add a timer to retry the computation request ? */ + + specialize_outgoing_path(pcc_state, req->path); + + PCEP_DEBUG( + "%s Sending computation request %d for path %s to %s (retry %d)", + pcc_state->tag, req->path->req_id, req->path->name, + ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)), + req->retry_count); + PCEP_DEBUG_PATH("%s Computation request path %s: %s", pcc_state->tag, + req->path->name, format_path(req->path)); + + msg = pcep_lib_format_request(&pcc_state->caps, req->path); + send_pcep_message(pcc_state, msg); + req->was_sent = true; + + /* TODO: Enable this back when the pcep config changes are merged back + */ + // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds; + timeout = 30; + pcep_thread_schedule_timeout(ctrl_state, pcc_state->id, + TO_COMPUTATION_REQUEST, timeout, + (void *)req, &req->t_retry); +} + +void cancel_comp_requests(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state) +{ + struct req_entry *req, *safe_req; + + RB_FOREACH_SAFE (req, req_entry_head, &pcc_state->requests, safe_req) { + cancel_comp_request(ctrl_state, pcc_state, req); + RB_REMOVE(req_entry_head, &pcc_state->requests, req); + remove_reqid_mapping(pcc_state, req->path); + free_req_entry(req); + } +} + +void cancel_comp_request(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct req_entry *req) +{ + char buff[40]; + struct pcep_message *msg; + + if (req->was_sent) { + /* TODO: Send a computation request cancelation + * notification to the PCE */ + pcep_thread_cancel_timer(&req->t_retry); + } + + PCEP_DEBUG( + "%s Canceling computation request %d for path %s to %s (retry %d)", + pcc_state->tag, req->path->req_id, req->path->name, + ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)), + req->retry_count); + PCEP_DEBUG_PATH("%s Canceled computation request path %s: %s", + pcc_state->tag, req->path->name, + format_path(req->path)); + + msg = pcep_lib_format_request_cancelled(req->path->req_id); + send_pcep_message(pcc_state, msg); +} + +void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey, + struct ipaddr *addr) +{ + select_pcc_addresses(pcc_state); + if (IS_IPADDR_V6(&nbkey->endpoint)) { + assert(CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)); + addr->ipa_type = IPADDR_V6; + addr->ipaddr_v6 = pcc_state->pcc_addr_v6; + } else if (IS_IPADDR_V4(&nbkey->endpoint)) { + assert(CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)); + addr->ipa_type = IPADDR_V4; + addr->ipaddr_v4 = pcc_state->pcc_addr_v4; + } else { + addr->ipa_type = IPADDR_NONE; + } +} + + +/* ------------ Data Structure Helper Functions ------------ */ + +void lookup_plspid(struct pcc_state *pcc_state, struct path *path) +{ + struct plspid_map_data key, *plspid_mapping; + struct nbkey_map_data *nbkey_mapping; + + if (path->nbkey.color != 0) { + key.nbkey = path->nbkey; + plspid_mapping = plspid_map_find(&pcc_state->plspid_map, &key); + if (plspid_mapping == NULL) { + plspid_mapping = + XCALLOC(MTYPE_PCEP, sizeof(*plspid_mapping)); + plspid_mapping->nbkey = key.nbkey; + plspid_mapping->plspid = pcc_state->next_plspid; + plspid_map_add(&pcc_state->plspid_map, plspid_mapping); + nbkey_mapping = + XCALLOC(MTYPE_PCEP, sizeof(*nbkey_mapping)); + nbkey_mapping->nbkey = key.nbkey; + nbkey_mapping->plspid = pcc_state->next_plspid; + nbkey_map_add(&pcc_state->nbkey_map, nbkey_mapping); + pcc_state->next_plspid++; + // FIXME: Send some error to the PCE isntead of crashing + assert(pcc_state->next_plspid <= 1048576); + } + path->plsp_id = plspid_mapping->plspid; + } +} + +void lookup_nbkey(struct pcc_state *pcc_state, struct path *path) +{ + struct nbkey_map_data key, *mapping; + // TODO: Should give an error to the PCE instead of crashing + assert(path->plsp_id != 0); + key.plspid = path->plsp_id; + mapping = nbkey_map_find(&pcc_state->nbkey_map, &key); + assert(mapping != NULL); + path->nbkey = mapping->nbkey; +} + +void free_req_entry(struct req_entry *req) +{ + pcep_free_path(req->path); + XFREE(MTYPE_PCEP, req); +} + +struct req_entry *push_new_req(struct pcc_state *pcc_state, struct path *path) +{ + struct req_entry *req; + + req = XCALLOC(MTYPE_PCEP, sizeof(*req)); + req->retry_count = 0; + req->path = pcep_copy_path(path); + repush_req(pcc_state, req); + + return req; +} + +void repush_req(struct pcc_state *pcc_state, struct req_entry *req) +{ + uint32_t reqid = pcc_state->next_reqid; + void *res; + + req->was_sent = false; + req->path->req_id = reqid; + res = RB_INSERT(req_entry_head, &pcc_state->requests, req); + assert(res == NULL); + assert(add_reqid_mapping(pcc_state, req->path) == true); + + pcc_state->next_reqid += 1; + /* Wrapping is allowed, but 0 is not a valid id */ + if (pcc_state->next_reqid == 0) + pcc_state->next_reqid = 1; +} + +struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid) +{ + struct path path = {.req_id = reqid}; + struct req_entry key = {.path = &path}; + struct req_entry *req; + + req = RB_FIND(req_entry_head, &pcc_state->requests, &key); + if (req == NULL) + return NULL; + RB_REMOVE(req_entry_head, &pcc_state->requests, req); + remove_reqid_mapping(pcc_state, req->path); + + return req; +} + +bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path) +{ + struct req_map_data *mapping; + mapping = XCALLOC(MTYPE_PCEP, sizeof(*mapping)); + mapping->nbkey = path->nbkey; + mapping->reqid = path->req_id; + if (req_map_add(&pcc_state->req_map, mapping) != NULL) { + XFREE(MTYPE_PCEP, mapping); + return false; + } + return true; +} + +void remove_reqid_mapping(struct pcc_state *pcc_state, struct path *path) +{ + struct req_map_data key, *mapping; + key.nbkey = path->nbkey; + mapping = req_map_find(&pcc_state->req_map, &key); + if (mapping != NULL) { + req_map_del(&pcc_state->req_map, mapping); + XFREE(MTYPE_PCEP, mapping); + } +} + +uint32_t lookup_reqid(struct pcc_state *pcc_state, struct path *path) +{ + struct req_map_data key, *mapping; + key.nbkey = path->nbkey; + mapping = req_map_find(&pcc_state->req_map, &key); + if (mapping != NULL) + return mapping->reqid; + return 0; +} + +bool has_pending_req_for(struct pcc_state *pcc_state, struct path *path) +{ + return lookup_reqid(pcc_state, path) != 0; +} + + +/* ------------ Data Structure Callbacks ------------ */ + +#define CMP_RETURN(A, B) \ + if (A != B) \ + return (A < B) ? -1 : 1 + +static uint32_t hash_nbkey(const struct lsp_nb_key *nbkey) +{ + uint32_t hash; + hash = jhash_2words(nbkey->color, nbkey->preference, 0x55aa5a5a); + switch (nbkey->endpoint.ipa_type) { + case IPADDR_V4: + return jhash(&nbkey->endpoint.ipaddr_v4, + sizeof(nbkey->endpoint.ipaddr_v4), hash); + case IPADDR_V6: + return jhash(&nbkey->endpoint.ipaddr_v6, + sizeof(nbkey->endpoint.ipaddr_v6), hash); + default: + return hash; + } +} + +static int cmp_nbkey(const struct lsp_nb_key *a, const struct lsp_nb_key *b) +{ + CMP_RETURN(a->color, b->color); + int cmp = ipaddr_cmp(&a->endpoint, &b->endpoint); + if (cmp != 0) + return cmp; + CMP_RETURN(a->preference, b->preference); + return 0; +} + +int plspid_map_cmp(const struct plspid_map_data *a, + const struct plspid_map_data *b) +{ + return cmp_nbkey(&a->nbkey, &b->nbkey); +} + +uint32_t plspid_map_hash(const struct plspid_map_data *e) +{ + return hash_nbkey(&e->nbkey); +} + +int nbkey_map_cmp(const struct nbkey_map_data *a, + const struct nbkey_map_data *b) +{ + CMP_RETURN(a->plspid, b->plspid); + return 0; +} + +uint32_t nbkey_map_hash(const struct nbkey_map_data *e) +{ + return e->plspid; +} + +int req_map_cmp(const struct req_map_data *a, const struct req_map_data *b) +{ + return cmp_nbkey(&a->nbkey, &b->nbkey); +} + +uint32_t req_map_hash(const struct req_map_data *e) +{ + return hash_nbkey(&e->nbkey); +} diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h new file mode 100644 index 000000000..a466d92d5 --- /dev/null +++ b/pathd/path_pcep_pcc.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PATH_PCEP_PCC_H_ +#define _PATH_PCEP_PCC_H_ + +#include "typesafe.h" +#include "pathd/path_pcep.h" + +enum pcc_status { + PCEP_PCC_INITIALIZED = 0, + PCEP_PCC_DISCONNECTED, + PCEP_PCC_CONNECTING, + PCEP_PCC_SYNCHRONIZING, + PCEP_PCC_OPERATING +}; + +PREDECL_HASH(plspid_map) +PREDECL_HASH(nbkey_map) +PREDECL_HASH(req_map) + +struct plspid_map_data { + struct plspid_map_item mi; + struct lsp_nb_key nbkey; + uint32_t plspid; +}; + +struct nbkey_map_data { + struct nbkey_map_item mi; + struct lsp_nb_key nbkey; + uint32_t plspid; +}; + +struct req_map_data { + struct req_map_item mi; + struct lsp_nb_key nbkey; + uint32_t reqid; +}; + +struct req_entry { + RB_ENTRY(req_entry) entry; + struct thread *t_retry; + int retry_count; + bool was_sent; + struct path *path; +}; +RB_HEAD(req_entry_head, req_entry); +RB_PROTOTYPE(req_entry_head, req_entry, entry, req_entry_compare); + +struct pcc_state { + int id; + char tag[MAX_TAG_SIZE]; + enum pcc_status status; + uint16_t flags; +#define F_PCC_STATE_HAS_IPV4 0x0002 +#define F_PCC_STATE_HAS_IPV6 0x0004 + struct pcc_opts *pcc_opts; + struct pce_opts *pce_opts; + struct in_addr pcc_addr_v4; + struct in6_addr pcc_addr_v6; + /* PCC transport source address */ + struct ipaddr pcc_addr_tr; + char *originator; + pcep_session *sess; + uint32_t retry_count; + bool synchronized; + struct thread *t_reconnect; + struct thread *t_update_best; + struct thread *t_session_timeout; + uint32_t next_reqid; + uint32_t next_plspid; + struct plspid_map_head plspid_map; + struct nbkey_map_head nbkey_map; + struct req_map_head req_map; + struct req_entry_head requests; + struct pcep_caps caps; + bool is_best; + bool previous_best; +}; + +struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state, + int pcc_id); +void pcep_pcc_finalize(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state); +int pcep_pcc_disable(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, + struct pcc_opts *pcc_opts, struct pce_opts *pce_opts); +void pcep_pcc_reconnect(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + pcep_event *event); +void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + enum pcep_pathd_event_type type, + struct path *path); +void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, + enum pcep_ctrl_timer_type type, void *param); +void pcep_pcc_sync_path(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct path *path); +void pcep_pcc_sync_done(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state); +void pcep_pcc_send_report(struct ctrl_state *ctrl_state, + struct pcc_state *pcc_state, struct path *path); +int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id, + struct pcc_state **pcc_state_list); +int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state, + struct pcc_state **pcc_state_list); +int pcep_pcc_timer_update_best_pce(struct ctrl_state *ctrl_state, int pcc_id); +int pcep_pcc_calculate_best_pce(struct pcc_state **pcc); +int pcep_pcc_get_pcc_id_by_ip_port(struct pcc_state **pcc, + struct pce_opts *pce_opts); +int pcep_pcc_get_pcc_id_by_idx(struct pcc_state **pcc, int idx); +struct pcc_state *pcep_pcc_get_pcc_by_id(struct pcc_state **pcc, int id); +struct pcc_state *pcep_pcc_get_pcc_by_name(struct pcc_state **pcc, + const char *pce_name); +int pcep_pcc_get_pcc_idx_by_id(struct pcc_state **pcc, int id); +int pcep_pcc_get_free_pcc_idx(struct pcc_state **pcc); +int pcep_pcc_get_pcc_id(struct pcc_state *pcc); +void pcep_pcc_copy_pcc_info(struct pcc_state **pcc, + struct pcep_pcc_info *pcc_info); + +#endif // _PATH_PCEP_PCC_H_ diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample index fc7acafc1..9fe7d2d6e 100644 --- a/pathd/pathd.conf.sample +++ b/pathd/pathd.conf.sample @@ -29,5 +29,13 @@ segment-routing metric te 10 ! ! + pcep + pcc-peer PCE1 + address ip 127.0.0.1 + sr-draft07 + ! + pcc + peer PCE1 + ! ! ! diff --git a/pathd/subdir.am b/pathd/subdir.am index 23399b9d8..7403787fb 100644 --- a/pathd/subdir.am +++ b/pathd/subdir.am @@ -10,6 +10,12 @@ vtysh_scan += $(top_srcdir)/pathd/path_cli.c vtysh_daemons += pathd # TODO add man page #man8 += $(MANBUILD)/pathd.8 + +if HAVE_PATHD_PCEP +vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c +module_LTLIBRARIES += pathd/pathd_pcep.la +endif + endif pathd_libpath_a_SOURCES = \ @@ -29,20 +35,45 @@ clippy_scan += \ pathd/path_cli.c \ # end +if HAVE_PATHD_PCEP +clippy_scan += \ + pathd/path_pcep_cli.c \ + # end +endif + noinst_HEADERS += \ pathd/path_debug.h \ pathd/path_errors.h \ pathd/path_memory.h \ pathd/path_nb.h \ + pathd/path_pcep.h \ + pathd/path_pcep_cli.h \ + pathd/path_pcep_controller.h \ + pathd/path_pcep_debug.h \ + pathd/path_pcep_lib.h \ + pathd/path_pcep_memory.h \ + pathd/path_pcep_config.h \ + pathd/path_pcep_pcc.h \ pathd/path_zebra.h \ pathd/pathd.h \ # end -pathd/path_cli_clippy.c: $(CLIPPY_DEPS) -pathd/path_cli.$(OBJEXT): pathd/path_cli_clippy.c - pathd_pathd_SOURCES = pathd/path_main.c nodist_pathd_pathd_SOURCES = \ yang/frr-pathd.yang.c \ # end pathd_pathd_LDADD = pathd/libpath.a lib/libfrr.la -lm $(LIBCAP) + +pathd_pathd_pcep_la_SOURCES = \ + pathd/path_pcep.c \ + pathd/path_pcep_cli.c \ + pathd/path_pcep_controller.c \ + pathd/path_pcep_debug.c \ + pathd/path_pcep_lib.c \ + pathd/path_pcep_memory.c \ + pathd/path_pcep_config.c \ + pathd/path_pcep_pcc.c \ + # end +pathd_pathd_pcep_la_CFLAGS = $(WERROR) +pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +pathd_pathd_pcep_la_LIBADD = @PATHD_PCEP_LIBS@ diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 137a3d5a0..21dc93954 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -681,6 +681,27 @@ end current_context_lines = [] new_ctx = False log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys) + + elif ( + line.startswith("peer ") + and len(ctx_keys) == 4 + and ctx_keys[0].startswith("segment-routing") + and ctx_keys[1].startswith("traffic-eng") + and ctx_keys[2].startswith("pcep") + and ctx_keys[3].startswith("pcc") + ): + # If there is no precedence, we add the default one (255) so + # the line is not removed and added back + m = re.search('peer ([^ ]*)', line) + if (m != None): + (name,) = m.groups() + line = "peer %s precedence 255" % (name,) + + current_context_lines.append(line) + log.debug( + "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys + ) + elif ( line.startswith("address-family ") or line.startswith("vnc defaults") @@ -805,6 +826,73 @@ end ) ctx_keys.append(line) + elif ( + line.startswith("pcep") + and len(ctx_keys) == 2 + and ctx_keys[0].startswith("segment-routing") + and ctx_keys[1].startswith("traffic-eng") + ): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + main_ctx_key = copy.deepcopy(ctx_keys) + log.debug( + "LINE %-50s: entering pcep sub-context, append to ctx_keys", line + ) + ctx_keys.append(line) + + elif ( + line.startswith("pce-config ") + and len(ctx_keys) == 3 + and ctx_keys[0].startswith("segment-routing") + and ctx_keys[1].startswith("traffic-eng") + and ctx_keys[2].startswith("pcep") + ): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + main_ctx_key = copy.deepcopy(ctx_keys) + log.debug( + "LINE %-50s: entering pce-config sub-context, append to ctx_keys", line + ) + ctx_keys.append(line) + + elif ( + line.startswith("pce ") + and len(ctx_keys) == 3 + and ctx_keys[0].startswith("segment-routing") + and ctx_keys[1].startswith("traffic-eng") + and ctx_keys[2].startswith("pcep") + ): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + main_ctx_key = copy.deepcopy(ctx_keys) + log.debug( + "LINE %-50s: entering pce sub-context, append to ctx_keys", line + ) + ctx_keys.append(line) + + elif ( + line.startswith("pcc") + and len(ctx_keys) == 3 + and ctx_keys[0].startswith("segment-routing") + and ctx_keys[1].startswith("traffic-eng") + and ctx_keys[2].startswith("pcep") + ): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + main_ctx_key = copy.deepcopy(ctx_keys) + log.debug( + "LINE %-50s: entering pcc sub-context, append to ctx_keys", line + ) + ctx_keys.append(line) + else: # Continuing in an existing context, add non-commented lines to it current_context_lines.append(line) @@ -1415,6 +1503,14 @@ def compare_context_objects(newconf, running): ): continue + # Neither the pcep command + elif ( + len(running_ctx_keys) == 3 + and running_ctx_keys[0].startswith('segment-routing') + and running_ctx_keys[2].startswith('pcep4') + ): + continue + # Segment lists can only be deleted after we removed all the candidate paths that # use them, so add them to a separate array that is going to be appended at the end elif ( diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 93a63cd96..22eff40cd 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -541,8 +541,12 @@ static int vtysh_execute_func(const char *line, int pager) vtysh_execute("exit"); } else if ((saved_node == SR_SEGMENT_LIST_NODE || saved_node == SR_POLICY_NODE - || saved_node == SR_CANDIDATE_DYN_NODE) - && (tried > 0)) { + || saved_node == SR_CANDIDATE_DYN_NODE + || saved_node == PCEP_NODE + || saved_node == PCEP_PCE_CONFIG_NODE + || saved_node == PCEP_PCE_NODE + || saved_node == PCEP_PCC_NODE) + && (tried > 0)) { vtysh_execute("exit"); } else if (tried) { vtysh_execute("end"); @@ -826,16 +830,24 @@ int vtysh_mark_file(const char *filename) && (tried == 1)) { vty_out(vty, "exit\n"); } else if (((prev_node == SEGMENT_ROUTING_NODE) - || (prev_node == SR_TRAFFIC_ENG_NODE) - || (prev_node == SR_SEGMENT_LIST_NODE) - || (prev_node == SR_POLICY_NODE) - || (prev_node == SR_CANDIDATE_DYN_NODE)) + || (prev_node == SR_TRAFFIC_ENG_NODE) + || (prev_node == SR_SEGMENT_LIST_NODE) + || (prev_node == SR_POLICY_NODE) + || (prev_node == SR_CANDIDATE_DYN_NODE) + || (prev_node == PCEP_NODE) + || (prev_node == PCEP_PCE_CONFIG_NODE) + || (prev_node == PCEP_PCE_NODE) + || (prev_node == PCEP_PCC_NODE)) && (tried > 0)) { ending = (vty->node != SEGMENT_ROUTING_NODE) - && (vty->node != SR_TRAFFIC_ENG_NODE) - && (vty->node != SR_SEGMENT_LIST_NODE) - && (vty->node != SR_POLICY_NODE) - && (vty->node != SR_CANDIDATE_DYN_NODE); + && (vty->node != SR_TRAFFIC_ENG_NODE) + && (vty->node != SR_SEGMENT_LIST_NODE) + && (vty->node != SR_POLICY_NODE) + && (vty->node != SR_CANDIDATE_DYN_NODE) + && (vty->node != PCEP_NODE) + && (vty->node != PCEP_PCE_CONFIG_NODE) + && (vty->node != PCEP_PCE_NODE) + && (vty->node != PCEP_PCC_NODE); if (ending) tried--; while (tried-- > 0) @@ -1284,6 +1296,34 @@ static struct cmd_node srte_candidate_dyn_node = { .prompt = "%s(config-sr-te-candidate)# ", }; +static struct cmd_node pcep_node = { + .name = "srte pcep", + .node = PCEP_NODE, + .parent_node = SR_TRAFFIC_ENG_NODE, + .prompt = "%s(config-sr-te-pcep)# " +}; + +static struct cmd_node pcep_pcc_node = { + .name = "srte pcep pcc", + .node = PCEP_PCC_NODE, + .parent_node = PCEP_NODE, + .prompt = "%s(config-sr-te-pcep-pcc)# ", +}; + +static struct cmd_node pcep_pce_node = { + .name = "srte pcep pce-peer", + .node = PCEP_PCE_NODE, + .parent_node = PCEP_NODE, + .prompt = "%s(config-sr-te-pcep-pce-peer)# ", +}; + +static struct cmd_node pcep_pce_config_node = { + .name = "srte pcep pce-config", + .node = PCEP_PCE_CONFIG_NODE, + .parent_node = PCEP_NODE, + .prompt = "%s(pcep-sr-te-pcep-pce-config)# ", +}; + static struct cmd_node vrf_node = { .name = "vrf", .node = VRF_NODE, @@ -2091,6 +2131,48 @@ DEFUNSH(VTYSH_PATHD, srte_policy_candidate_dyn_path, vty->node = SR_CANDIDATE_DYN_NODE; return CMD_SUCCESS; } + +#if defined(HAVE_PATHD_PCEP) + +DEFUNSH(VTYSH_PATHD, pcep, pcep_cmd, + "pcep", + "Configure SR pcep\n") +{ + vty->node = PCEP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd, + "[no] pcc", + NO_STR + "PCC configuration\n") +{ + vty->node = PCEP_PCC_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, pcep_cli_pce, pcep_cli_pce_cmd, + "[no] pce WORD", + NO_STR + "PCE configuration\n" + "Peer name\n") +{ + vty->node = PCEP_PCE_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, pcep_cli_pcep_pce_config, pcep_cli_pcep_pce_config_cmd, + "[no] pce-config WORD", + NO_STR + "PCEP peer Configuration Group\n" + "PCEP peer Configuration Group name\n") +{ + vty->node = PCEP_PCE_CONFIG_NODE; + return CMD_SUCCESS; +} + +#endif /* HAVE_PATHD_PCEP */ + #endif /* HAVE_PATHD */ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, @@ -4304,6 +4386,33 @@ void vtysh_init_vty(void) install_element(SR_TRAFFIC_ENG_NODE, &srte_segment_list_cmd); install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd); install_element(SR_POLICY_NODE, &srte_policy_candidate_dyn_path_cmd); + +#if defined(HAVE_PATHD_PCEP) + install_node(&pcep_node); + install_node(&pcep_pcc_node); + install_node(&pcep_pce_node); + install_node(&pcep_pce_config_node); + + install_element(PCEP_NODE, &vtysh_exit_pathd_cmd); + install_element(PCEP_NODE, &vtysh_quit_pathd_cmd); + install_element(PCEP_PCC_NODE, &vtysh_exit_pathd_cmd); + install_element(PCEP_PCC_NODE, &vtysh_quit_pathd_cmd); + install_element(PCEP_PCE_NODE, &vtysh_exit_pathd_cmd); + install_element(PCEP_PCE_NODE, &vtysh_quit_pathd_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &vtysh_exit_pathd_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &vtysh_quit_pathd_cmd); + + install_element(PCEP_NODE, &vtysh_end_all_cmd); + install_element(PCEP_PCC_NODE, &vtysh_end_all_cmd); + install_element(PCEP_PCE_NODE, &vtysh_end_all_cmd); + install_element(PCEP_PCE_CONFIG_NODE, &vtysh_end_all_cmd); + + install_element(SR_TRAFFIC_ENG_NODE, &pcep_cmd); + install_element(PCEP_NODE, &pcep_cli_pcc_cmd); + install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd); + install_element(PCEP_NODE, &pcep_cli_pce_cmd); +#endif /* HAVE_PATHD_PCEP */ + #endif /* HAVE_PATHD */ /* keychain */ |