diff options
81 files changed, 7828 insertions, 12 deletions
diff --git a/Makefile.am b/Makefile.am index b6e8a2bce8..90c8407010 100644 --- a/Makefile.am +++ b/Makefile.am @@ -158,6 +158,7 @@ include bfdd/subdir.am  include yang/subdir.am  include yang/libyang_plugins/subdir.am  include vrrpd/subdir.am +include pathd/subdir.am  include vtysh/subdir.am  include tests/subdir.am diff --git a/configure.ac b/configure.ac index acedf7573d..168a9041df 100755 --- a/configure.ac +++ b/configure.ac @@ -560,6 +560,8 @@ AC_ARG_ENABLE([fabricd],    AS_HELP_STRING([--disable-fabricd], [do not build fabricd]))  AC_ARG_ENABLE([vrrpd],    AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd])) +AC_ARG_ENABLE([pathd], +  AS_HELP_STRING([--disable-pathd], [do not build pathd]))  AC_ARG_ENABLE([bgp-announce],    AS_HELP_STRING([--disable-bgp-announce], [turn off BGP route announcement]))  AC_ARG_ENABLE([bgp-vnc], @@ -1675,6 +1677,10 @@ else    esac  fi +AS_IF([test "$enable_pathd" != "no"], [ +  AC_DEFINE([HAVE_PATHD], [1], [pathd]) +]) +  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 @@ -2489,6 +2495,7 @@ AM_CONDITIONAL([SHARPD], [test "$enable_sharpd" = "yes"])  AM_CONDITIONAL([STATICD], [test "$enable_staticd" != "no"])  AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"])  AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"]) +AM_CONDITIONAL([PATHD], [test "$enable_pathd" != "no"])  AC_CONFIG_FILES([Makefile],[  	test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build" diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 5a7da806ff..60a7d505a7 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -18,3 +18,4 @@ FRRouting Developer's Guide     ospf     zebra     vtysh +   pathd diff --git a/doc/developer/path-internals-daemon.rst b/doc/developer/path-internals-daemon.rst new file mode 100644 index 0000000000..29f017284f --- /dev/null +++ b/doc/developer/path-internals-daemon.rst @@ -0,0 +1,115 @@ +PATHD Internals +=============== + +Architecture +------------ + +Overview +........ + +The pathd deamon manages the segment routing policies, it owns the data +structures representing them and can load modules that manipulate them like the +PCEP module. Its responsibility is to select a candidate path for each +configured policy and to install it into Zebra. + +Zebra +..... + +Zebra manages policies that are active or pending to be activated due to the +next hop not being available yet. In zebra, policy data structures and APIs are +defined in `zebra_srte.[hc]`. + +The responsibilities of Zebra are: + + - Store the policies' segment list. + - Install the policies when their next-hop is available. + - Notify other daemons of the status of the policies. + +Adding and removing policies is done using the commands `ZEBRA_SR_POLICY_SET` +and `ZEBRA_SR_POLICY_DELETE` as parameter of the function `zebra_send_sr_policy` +all defined in `zclient.[hc]`. + +If the first segment of the policy is an unknown label, it is kept until +notified by the mpls hooks `zebra_mpls_label_created`, and then it is installed. + +To get notified when a policy status changes, a client can implement the +`sr_policy_notify_status` callback defined in `zclient.[hc]`. + +For encoding/decoding the various data structures used to comunicate with zebra, +the following functions are available from `zclient.[hc]`: +`zapi_sr_policy_encode`, `zapi_sr_policy_decode` and +`zapi_sr_policy_notify_status_decode`. + + +Pathd +..... + + +The pathd daemon manages all the possible candidate paths for the segment +routing policies and selects the best one following the +`segment routing policy draft <https://tools.ietf.org/html/draft-ietf-spring-segment-routing-policy-06#section-2.9>`_. +It also supports loadable modules for handling dynamic candidate paths and the +creation of new policies and candidate paths at runtime. + +The responsibilities of the pathd base daemon, not including any optional +modules, are: + + - Store the policies and all the possible candidate paths for them. + - Select the best candidate path for each policy and send it to Zebra. + - Provide VTYSH configuration to set up policies and candidate paths. + - Provide a Northbound API to manipulate **configured** policies and candidate paths. + - Handle loadable modules for extending the functionality. + - Provide an API to the loadable module to manipulate policies and candidate paths. + + +Threading Model +--------------- + +The daemon runs completely inside the main thread using FRR event model, there +is no threading involved. + + +Source Code +----------- + +Internal Data Structures +........................ + +The main data structures for policies and candidate paths are defined in +`pathd.h` and implemented in `pathd.c`. + +When modifying these structures, either directly or through the functions +exported by `pathd.h`, nothing should be deleted/freed right away. The deletion +or modification flags must be set and when all the changes are done, the +function `srte_apply_changes` must be called. When called, a new candidate path +may be elected and sent to Zebra, and all the structures flagged as deleted +will be freed. In addition, a hook will be called so dynamic modules can perform +any required action when the elected candidate path changes. + + +Northbound API +.............. + +The northbound API is defined in `path_nb.[ch]` and implemented in +`path_nb_config.c` for configuration data and `path_nb_state.c` for operational +data. + + +Command Line Client +................... + +The command-line client (VTYSH) is implemented in `path_cli.c`. + + +Interface with Zebra +.................... + +All the functions interfacing with Zebra are defined and implemented in +`path_zebra.[hc]`. + + +Loadable Module API +................... + +For the time being, the API the loadable module uses is defined by `pathd.h`, +but in the future, it should be moved to a dedicated include file. diff --git a/doc/developer/path-internals.rst b/doc/developer/path-internals.rst new file mode 100644 index 0000000000..980d359453 --- /dev/null +++ b/doc/developer/path-internals.rst @@ -0,0 +1,10 @@ +.. _path_internals: + +********* +Internals +********* + +.. toctree:: +   :maxdepth: 2 + +   path-internals-daemon diff --git a/doc/developer/path.rst b/doc/developer/path.rst new file mode 100644 index 0000000000..b6d2438c58 --- /dev/null +++ b/doc/developer/path.rst @@ -0,0 +1,11 @@ +.. _path: + +***** +PATHD +***** + +.. toctree:: +   :maxdepth: 2 + +   path-internals + diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 3c0d203007..400555a2e8 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -46,6 +46,9 @@ dev_RSTFILES = \  	doc/developer/packaging-debian.rst \  	doc/developer/packaging-redhat.rst \  	doc/developer/packaging.rst \ +	doc/developer/path-internals-daemon.rst \ +	doc/developer/path-internals.rst \ +	doc/developer/path.rst \  	doc/developer/rcu.rst \  	doc/developer/static-linking.rst \  	doc/developer/testing.rst \ diff --git a/doc/user/index.rst b/doc/user/index.rst index 8ac997f8dd..993acf3b4c 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -50,6 +50,7 @@ Protocols     nhrpd     ospfd     ospf6d +   pathd     pim     pbr     ripd diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst new file mode 100644 index 0000000000..0c825d423b --- /dev/null +++ b/doc/user/pathd.rst @@ -0,0 +1,233 @@ +.. _path: + +**** +PATH +**** + +:abbr:`PATH` is a daemon that handles the installation and deletion +of Segment Routing (SR) Policies. + + +.. _starting-path: + +Starting PATH +============= + +Default configuration file for *pathd* is :file:`pathd.conf`.  The typical +location of :file:`pathd.conf` is |INSTALL_PREFIX_ETC|/pathd.conf. + +If the user is using integrated config, then :file:`pathd.conf` need not be +present and the :file:`frr.conf` is read instead. + +.. program:: pathd + +:abbr:`PATH` supports all the common FRR daemon start options which are +documented elsewhere. + + +Pathd Configuration +=================== + +Example: + +.. code-block:: frr + +  segment-routing +   traffic-eng +    segment-list SL1 +     index 10 mpls label 16010 +     index 20 mpls label 16030 +    ! +    policy color 1 endpoint 1.1.1.1 +     name default +     binding-sid 4000 +     candidate-path preference 100 name CP1 explicit segment-list SL1 +     candidate-path preference 200 name CP2 dynamic +      affinity include-any 0x000000FF +      bandwidth 100000 +      metric bound msd 16 required +      metric te 10 +      objective-function mcp required +    ! +   ! +  ! + + +.. _path-commands: + +Configuration Commands +---------------------- + +.. index:: segment-routing +.. clicmd:: segment-routing + +   Configure segment routing. + +.. index:: traffic-eng +.. clicmd:: traffic-eng + +   Configure segment routing traffic engineering. + +.. index:: [no] segment-list NAME +.. clicmd:: [no] segment-list NAME + +   Delete or start a segment list definition. + + +.. index:: [no] index INDEX mpls label LABEL [nai node ADDRESS] +.. clicmd:: [no] index INDEX mpls label LABEL [nai node ADDRESS] + +   Delete or specify a segment in a segment list definition. + + +.. index:: [no] policy color COLOR endpoint ENDPOINT +.. clicmd:: [no] policy color COLOR endpoint ENDPOINT + +   Delete or start a policy definition. + + +.. index:: name NAME +.. clicmd:: name NAME + +   Specify the policy name. + + +.. index:: binding-sid LABEL +.. clicmd:: binding-sid LABEL + +   Specify the policy SID. + + +.. index:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME +.. clicmd:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME + +   Delete or define an explicit candidate path. + + +.. index:: [no] candidate-path preference PREFERENCE name NAME dynamic +.. clicmd:: [no] candidate-path preference PREFERENCE name NAME dynamic + +   Delete or start a dynamic candidate path definition. + + +.. index:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN +.. clicmd:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN + +   Delete or specify an affinity constraint for a dynamic candidate path. + + +.. index:: [no] bandwidth BANDWIDTH [required] +.. clicmd:: [no] bandwidth BANDWIDTH [required] + +   Delete or specify a bandwidth constraint for a dynamic candidate path. + + +.. index:: [no] metric [bound] METRIC VALUE [required] +.. clicmd:: [no] metric [bound] METRIC VALUE [required] + +   Delete or specify a metric constraint for a dynamic candidate path. + +   The possible metrics are: +    - igp: IGP metric +    - te: TE metric +    - hc: Hop Counts +    - abc: Aggregate bandwidth consumption +    - mll: Load of the most loaded link +    - igp: Cumulative IGP cost +    - cte: Cumulative TE cost +    - igp: P2MP IGP metric +    - pte: P2MP TE metric +    - phc: P2MP hop count metric +    - msd: Segment-ID (SID) Depth +    - pd: Path Delay metric +    - pdv: Path Delay Variation metric +    - pl: Path Loss metric +    - ppd: P2MP Path Delay metric +    - pdv: P2MP Path Delay variation metric +    - ppl: P2MP Path Loss metric +    - nap: Number of adaptations on a path +    - nlp: Number of layers on a path +    - dc: Domain Count metric +    - bnc: Border Node Count metric + + +.. index:: [no] objective-function OBJFUN1 [required] +.. clicmd:: [no] objective-function OBJFUN1 [required] + +   Delete or specify a PCEP objective function constraint for a dynamic +   candidate path. + +   The possible functions are: +     - mcp: Minimum Cost Path [RFC5541] +     - mlp: Minimum Load Path [RFC5541] +     - mbp: Maximum residual Bandwidth Path [RFC5541] +     - mbc: Minimize aggregate Bandwidth Consumption [RFC5541] +     - mll: Minimize the Load of the most loaded Link [RFC5541] +     - mcc: Minimize the Cumulative Cost of a set of paths [RFC5541] +     - spt: Shortest Path Tree [RFC8306] +     - mct: Minimum Cost Tree [RFC8306] +     - mplp: Minimum Packet Loss Path [RFC8233] +     - mup: Maximum Under-Utilized Path [RFC8233] +     - mrup: Maximum Reserved Under-Utilized Path [RFC8233] +     - mtd: Minimize the number of Transit Domains [RFC8685] +     - mbn: Minimize the number of Border Nodes [RFC8685] +     - mctd: Minimize the number of Common Transit Domains [RFC8685] +     - msl: Minimize the number of Shared Links [RFC8800] +     - mss: Minimize the number of Shared SRLGs [RFC8800] +     - msn: Minimize the number of Shared Nodes [RFC8800] + + +Introspection Commands +---------------------- + +.. index:: show sr-te policy [detail] +.. clicmd:: show sr-te policy [detail] + +   Display the segment routing policies. + +.. code-block:: frr + +  router# show sr-te policy + +   Endpoint  Color  Name     BSID  Status +   ------------------------------------------ +   1.1.1.1   1      default  4000  Active + + +.. code-block:: frr + +  router# show sr-te policy detail + +  Endpoint: 1.1.1.1  Color: 1  Name: LOW_DELAY  BSID: 4000  Status: Active +      Preference: 100  Name: cand1  Type: explicit  Segment-List: sl1  Protocol-Origin: Local +    * Preference: 200  Name: cand1  Type: dynamic  Segment-List: 32453452  Protocol-Origin: PCEP + +The asterisk (*) marks the best, e.g. active, candidate path. Note that for segment-lists which are +retrieved via PCEP a random number based name is generated. + + +Usage with BGP route-maps +========================= + +It is possible to steer traffic 'into' a segment routing policy for routes +learned through BGP using route-maps: + +.. code-block:: frr + +  route-map SET_SR_POLICY permit 10 +   set sr-te color 1 +  ! +  router bgp 1 +   bgp router-id 2.2.2.2 +   neighbor 1.1.1.1 remote-as 1 +   neighbor 1.1.1.1 update-source lo +   ! +   address-family ipv4 unicast +    neighbor 1.1.1.1 next-hop-self +    neighbor 1.1.1.1 route-map SET_SR_POLICY in +    redistribute static +   exit-address-family +   ! +  ! + +In this case, the SR Policy with color `1` and endpoint `1.1.1.1` is selected. diff --git a/doc/user/subdir.am b/doc/user/subdir.am index dd7a193e34..a78d261863 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -27,6 +27,7 @@ user_RSTFILES = \  	doc/user/ospf_fundamentals.rst \  	doc/user/overview.rst \  	doc/user/packet-dumps.rst \ +	doc/user/pathd.rst \  	doc/user/pim.rst \  	doc/user/ripd.rst \  	doc/user/pbr.rst \ diff --git a/lib/command.c b/lib/command.c index 87110157f6..eeb14b734d 100644 --- a/lib/command.c +++ b/lib/command.c @@ -863,6 +863,18 @@ enum node_type node_parent(enum node_type node)  	case BFD_PROFILE_NODE:  		ret = BFD_NODE;  		break; +	case SR_TRAFFIC_ENG_NODE: +		ret = SEGMENT_ROUTING_NODE; +		break; +	case SR_SEGMENT_LIST_NODE: +		ret = SR_TRAFFIC_ENG_NODE; +		break; +	case SR_POLICY_NODE: +		ret = SR_TRAFFIC_ENG_NODE; +		break; +	case SR_CANDIDATE_DYN_NODE: +		ret = SR_POLICY_NODE; +		break;  	default:  		ret = CONFIG_NODE;  		break; diff --git a/lib/command.h b/lib/command.h index 1b0504101c..2eae9a1b35 100644 --- a/lib/command.h +++ b/lib/command.h @@ -145,6 +145,11 @@ enum node_type {  	PROTOCOL_NODE,		 /* protocol filtering node */  	MPLS_NODE,		 /* MPLS config node */  	PW_NODE,		 /* Pseudowire config node */ +	SEGMENT_ROUTING_NODE,	 /* Segment routing root node */ +	SR_TRAFFIC_ENG_NODE,	 /* SR Traffic Engineering node */ +	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 */  	VTY_NODE,		 /* Vty node. */  	FPM_NODE,		 /* Dataplane FPM node. */  	LINK_PARAMS_NODE,	/* Link-parameters node */ diff --git a/lib/ferr.h b/lib/ferr.h index a89b595e87..4e95431cea 100644 --- a/lib/ferr.h +++ b/lib/ferr.h @@ -132,6 +132,8 @@ struct ferr {  #define VTYSH_FRR_END       0x0FFFFFFF  #define WATCHFRR_FERR_START 0x10000001  #define WATCHFRR_FERR_END   0x10FFFFFF +#define PATH_FERR_START     0x11000001 +#define PATH_FERR_END       0x11FFFFFF  #define ZEBRA_FERR_START    0xF1000001  #define ZEBRA_FERR_END      0xF1FFFFFF  #define END_FERR            0xFFFFFFFF diff --git a/lib/yang.c b/lib/yang.c index a3e2a395d7..383dc9f5eb 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -92,6 +92,7 @@ static const char *const frr_native_modules[] = {  	"frr-isisd",  	"frr-vrrpd",  	"frr-zebra", +	"frr-pathd",  };  /* clang-format on */ diff --git a/pathd/.gitignore b/pathd/.gitignore new file mode 100644 index 0000000000..95f4a99794 --- /dev/null +++ b/pathd/.gitignore @@ -0,0 +1,2 @@ +libpath.a +pathd diff --git a/pathd/Makefile b/pathd/Makefile new file mode 100644 index 0000000000..b681a9ab1c --- /dev/null +++ b/pathd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS +	@$(MAKE) -s -C .. pathd/pathd +%: ALWAYS +	@$(MAKE) -s -C .. pathd/$@ + +Makefile: +	#nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/pathd/path_cli.c b/pathd/path_cli.c new file mode 100644 index 0000000000..a55e6ba406 --- /dev/null +++ b/pathd/path_cli.c @@ -0,0 +1,1108 @@ +/* + * 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 <float.h> +#include <math.h> +#include <zebra.h> + +#include "log.h" +#include "command.h" +#include "mpls.h" +#include "northbound_cli.h" +#include "termtable.h" + +#include "pathd/pathd.h" +#include "pathd/path_nb.h" +#include "pathd/path_memory.h" +#ifndef VTYSH_EXTRACT_PL +#include "pathd/path_cli_clippy.c" +#endif + +#define XPATH_MAXATTRSIZE 64 +#define XPATH_MAXKEYSIZE 42 +#define XPATH_POLICY_BASELEN 100 +#define XPATH_POLICY_MAXLEN (XPATH_POLICY_BASELEN + XPATH_MAXATTRSIZE) +#define XPATH_CANDIDATE_BASELEN (XPATH_POLICY_BASELEN + XPATH_MAXKEYSIZE) +#define XPATH_CANDIDATE_MAXLEN (XPATH_CANDIDATE_BASELEN + XPATH_MAXATTRSIZE) + + +static int config_write_segment_routing(struct vty *vty); +static int config_write_traffic_eng(struct vty *vty); +static int config_write_segment_lists(struct vty *vty); +static int config_write_sr_policies(struct vty *vty); + +DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client") + +/* Vty node structures. */ +static struct cmd_node segment_routing_node = { +	.name = "segment-routing", +	.node = SEGMENT_ROUTING_NODE, +	.parent_node = CONFIG_NODE, +	.prompt = "%s(config-sr)# ", +	.config_write = config_write_segment_routing, +}; + +static struct cmd_node sr_traffic_eng_node = { +	.name = "sr traffic-eng", +	.node = SR_TRAFFIC_ENG_NODE, +	.parent_node = SEGMENT_ROUTING_NODE, +	.prompt = "%s(config-sr-te)# ", +	.config_write = config_write_traffic_eng, +}; + +static struct cmd_node srte_segment_list_node = { +	.name = "srte segment-list", +	.node = SR_SEGMENT_LIST_NODE, +	.parent_node = SR_TRAFFIC_ENG_NODE, +	.prompt = "%s(config-sr-te-segment-list)# ", +	.config_write = config_write_segment_lists, +}; + +static struct cmd_node srte_policy_node = { +	.name = "srte policy", +	.node = SR_POLICY_NODE, +	.parent_node = SR_TRAFFIC_ENG_NODE, +	.prompt = "%s(config-sr-te-policy)# ", +	.config_write = config_write_sr_policies, +}; + +static struct cmd_node srte_candidate_dyn_node = { +	.name = "srte candidate-dyn", +	.node = SR_CANDIDATE_DYN_NODE, +	.parent_node = SR_POLICY_NODE, +	.prompt = "%s(config-sr-te-candidate)# ", +}; + + +/* + * Show SR-TE info + */ +DEFPY(show_srte_policy, +      show_srte_policy_cmd, +      "show sr-te policy", +      SHOW_STR +      "SR-TE info\n" +      "SR-TE Policy\n") +{ +	struct ttable *tt; +	struct srte_policy *policy; +	char *table; + +	if (RB_EMPTY(srte_policy_head, &srte_policies)) { +		vty_out(vty, "No SR Policies to display.\n\n"); +		return CMD_SUCCESS; +	} + +	/* Prepare table. */ +	tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); +	ttable_add_row(tt, "Endpoint|Color|Name|BSID|Status"); +	tt->style.cell.rpad = 2; +	tt->style.corner = '+'; +	ttable_restyle(tt); +	ttable_rowseps(tt, 0, BOTTOM, true, '-'); + +	RB_FOREACH (policy, srte_policy_head, &srte_policies) { +		char endpoint[46]; +		char binding_sid[16] = "-"; + +		ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +		if (policy->binding_sid != MPLS_LABEL_NONE) +			snprintf(binding_sid, sizeof(binding_sid), "%u", +				 policy->binding_sid); + +		ttable_add_row(tt, "%s|%u|%s|%s|%s", endpoint, policy->color, +			       policy->name, binding_sid, +			       policy->status == SRTE_POLICY_STATUS_UP +				       ? "Active" +				       : "Inactive"); +	} + +	/* Dump the generated table. */ +	table = ttable_dump(tt, "\n"); +	vty_out(vty, "%s\n", table); +	XFREE(MTYPE_TMP, table); + +	ttable_del(tt); + +	return CMD_SUCCESS; +} + +/* + * Show detailed SR-TE info + */ +DEFPY(show_srte_policy_detail, +      show_srte_policy_detail_cmd, +      "show sr-te policy detail", +      SHOW_STR +      "SR-TE info\n" +      "SR-TE Policy\n" +      "Show a detailed summary\n") +{ +	struct srte_policy *policy; + +	if (RB_EMPTY(srte_policy_head, &srte_policies)) { +		vty_out(vty, "No SR Policies to display.\n\n"); +		return CMD_SUCCESS; +	} + +	vty_out(vty, "\n"); +	RB_FOREACH (policy, srte_policy_head, &srte_policies) { +		struct srte_candidate *candidate; +		char endpoint[46]; +		char binding_sid[16] = "-"; +		char *segment_list_info; +		static char undefined_info[] = "(undefined)"; +		static char created_by_pce_info[] = "(created by PCE)"; + + +		ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +		if (policy->binding_sid != MPLS_LABEL_NONE) +			snprintf(binding_sid, sizeof(binding_sid), "%u", +				 policy->binding_sid); +		vty_out(vty, +			"Endpoint: %s  Color: %u  Name: %s  BSID: %s  Status: %s\n", +			endpoint, policy->color, policy->name, binding_sid, +			policy->status == SRTE_POLICY_STATUS_UP ? "Active" +								: "Inactive"); + +		RB_FOREACH (candidate, srte_candidate_head, +			    &policy->candidate_paths) { +			struct srte_segment_list *segment_list; + +			segment_list = candidate->lsp->segment_list; +			if (segment_list == NULL) +				segment_list_info = undefined_info; +			else if (segment_list->protocol_origin +				 == SRTE_ORIGIN_PCEP) +				segment_list_info = created_by_pce_info; +			else +				segment_list_info = +					candidate->lsp->segment_list->name; + +			vty_out(vty, +				"  %s Preference: %d  Name: %s  Type: %s  Segment-List: %s  Protocol-Origin: %s\n", +				CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST) +					? "*" +					: " ", +				candidate->preference, candidate->name, +				candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT +					? "explicit" +					: "dynamic", +				segment_list_info, +				srte_origin2str( +					candidate->lsp->protocol_origin)); +		} + +		vty_out(vty, "\n"); +	} + +	return CMD_SUCCESS; +} + +DEFPY_NOSH( +      segment_routing_list, +      segment_routing_cmd, +      "segment-routing", +      "Configure segment routing\n") +{ +	VTY_PUSH_CONTEXT_NULL(SEGMENT_ROUTING_NODE); +	return CMD_SUCCESS; +} + +DEFPY_NOSH( +      sr_traffic_eng_list, +      sr_traffic_eng_cmd, +      "traffic-eng", +      "Configure SR traffic engineering\n") +{ +	VTY_PUSH_CONTEXT_NULL(SR_TRAFFIC_ENG_NODE); +	return CMD_SUCCESS; +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list + */ +DEFPY_NOSH( +      srte_segment_list, +      srte_segment_list_cmd, +      "segment-list WORD$name", +      "Segment List\n" +      "Segment List Name\n") +{ +	char xpath[XPATH_MAXLEN]; +	int ret; + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/segment-list[name='%s']", name); +	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/segment-list[name='%s']/protocol-origin", +		 name); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "local"); + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/segment-list[name='%s']/originator", name); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "config"); + +	ret = nb_cli_apply_changes(vty, NULL); +	if (ret == CMD_SUCCESS) { +		snprintf(xpath, sizeof(xpath), +			 "/frr-pathd:pathd/srte/segment-list[name='%s']", name); +		VTY_PUSH_XPATH(SR_SEGMENT_LIST_NODE, xpath); +	} + +	return ret; +} + +DEFPY(srte_no_segment_list, +      srte_no_segment_list_cmd, +      "no segment-list WORD$name", +      NO_STR +      "Segment List\n" +      "Segment List Name\n") +{ +	char xpath[XPATH_MAXLEN]; + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/segment-list[name='%s']", name); +	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, +				bool show_defaults) +{ +	vty_out(vty, "  segment-list %s\n", +		yang_dnode_get_string(dnode, "./name")); +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list/segment + */ +DEFPY(srte_segment_list_segment, +      srte_segment_list_segment_cmd, +      "index (0-4294967295)$index mpls label (16-1048575)$label " +      "[nai$has_nai <" +      "node <A.B.C.D$node_ipv4|X:X::X:X$node_ipv6>" +      ">]", +      "Index\n" +      "Index Value\n" +      "MPLS or IP Label\n" +      "Label\n" +      "Label Value\n" +      "Segment NAI\n" +      "NAI node identifier\n" +      "NAI IPv4 node identifier\n" +      "NAI IPv6 node identifier\n") +{ +	char xpath[XPATH_MAXLEN]; +	const char *node_id; + +	snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str); +	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + +	snprintf(xpath, sizeof(xpath), "./segment[index='%s']/sid-value", +		 index_str); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str); + +	if (has_nai != NULL) { +		snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type", +			 index_str); +		if (node_ipv4_str != NULL) { +			nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, +					      "ipv4_node"); +			node_id = node_ipv4_str; +		} else if (node_ipv6_str != NULL) { +			nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, +					      "ipv6_node"); +			node_id = node_ipv6_str; +		} else { +			return CMD_ERR_NO_MATCH; +		} +		snprintf(xpath, sizeof(xpath), +			 "./segment[index='%s']/nai/local-address", index_str); +		nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id); +	} else { +		snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai", +			 index_str); +		nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); +	} + +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_segment_list_no_segment, +      srte_segment_list_no_segment_cmd, +      "no index (0-4294967295)$index", +      NO_STR +      "Index\n" +      "Index Value\n") +{ +	char xpath[XPATH_MAXLEN]; + +	snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str); +	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_srte_segment_list_segment(struct vty *vty, +					struct lyd_node *dnode, +					bool show_defaults) +{ +	vty_out(vty, "   index %s mpls label %s", +		yang_dnode_get_string(dnode, "./index"), +		yang_dnode_get_string(dnode, "./sid-value")); +	if (yang_dnode_exists(dnode, "./nai")) { +		struct ipaddr addr; +		switch (yang_dnode_get_enum(dnode, "./nai/type")) { +		case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: +			yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); +			vty_out(vty, " nai node %pI4", &addr.ipaddr_v4); +			break; +		case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: +			yang_dnode_get_ip(&addr, dnode, "./nai/local-address"); +			vty_out(vty, " nai node %pI6", &addr.ipaddr_v6); +			break; +		default: +			break; +		} +	} +	vty_out(vty, "\n"); +} + +/* + * XPath: /frr-pathd:pathd/policy + */ +DEFPY_NOSH( +	srte_policy, +	srte_policy_cmd, +	"policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint", +	"Segment Routing Policy\n" +	"SR Policy color\n" +	"SR Policy color value\n" +	"SR Policy endpoint\n" +	"SR Policy endpoint IPv4 address\n" +	"SR Policy endpoint IPv6 address\n") +{ +	char xpath[XPATH_POLICY_BASELEN]; +	int ret; + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']", +		 num_str, endpoint_str); +	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + +	ret = nb_cli_apply_changes(vty, NULL); +	if (ret == CMD_SUCCESS) +		VTY_PUSH_XPATH(SR_POLICY_NODE, xpath); + +	return ret; +} + +DEFPY(srte_no_policy, +      srte_no_policy_cmd, +      "no policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint", +      NO_STR +      "Segment Routing Policy\n" +      "SR Policy color\n" +      "SR Policy color value\n" +      "SR Policy endpoint\n" +      "SR Policy endpoint IPv4 address\n" +      "SR Policy endpoint IPv6 address\n") +{ +	char xpath[XPATH_POLICY_BASELEN]; + +	snprintf(xpath, sizeof(xpath), +		 "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']", +		 num_str, endpoint_str); +	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode, +			  bool show_defaults) +{ +	vty_out(vty, "  policy color %s endpoint %s\n", +		yang_dnode_get_string(dnode, "./color"), +		yang_dnode_get_string(dnode, "./endpoint")); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/name + */ +DEFPY(srte_policy_name, +      srte_policy_name_cmd, +      "name WORD$name", +      "Segment Routing Policy name\n" +      "SR Policy name value\n") +{ +	nb_cli_enqueue_change(vty, "./name", NB_OP_CREATE, name); + +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_policy_no_name, +      srte_policy_no_name_cmd, +      "no name [WORD]", +      NO_STR +      "Segment Routing Policy name\n" +      "SR Policy name value\n") +{ +	nb_cli_enqueue_change(vty, "./name", NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, NULL); +} + + +void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode, +				     bool show_defaults) +{ +	vty_out(vty, "   name %s\n", yang_dnode_get_string(dnode, NULL)); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/binding-sid + */ +DEFPY(srte_policy_binding_sid, +      srte_policy_binding_sid_cmd, +      "binding-sid (16-1048575)$label", +      "Segment Routing Policy Binding-SID\n" +      "SR Policy Binding-SID label\n") +{ +	nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_CREATE, label_str); + +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_policy_no_binding_sid, +      srte_policy_no_binding_sid_cmd, +      "no binding-sid [(16-1048575)]", +      NO_STR +      "Segment Routing Policy Binding-SID\n" +      "SR Policy Binding-SID label\n") +{ +	nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_srte_policy_binding_sid(struct vty *vty, +				      struct lyd_node *dnode, +				      bool show_defaults) +{ +	vty_out(vty, "   binding-sid %s\n", yang_dnode_get_string(dnode, NULL)); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path + */ +DEFPY(srte_policy_candidate_exp, +      srte_policy_candidate_exp_cmd, +      "candidate-path preference (0-4294967295)$preference name WORD$name \ +	 explicit segment-list WORD$list_name", +      "Segment Routing Policy Candidate Path\n" +      "Segment Routing Policy Candidate Path Preference\n" +      "Administrative Preference\n" +      "Segment Routing Policy Candidate Path Name\n" +      "Symbolic Name\n" +      "Explicit Path\n" +      "List of SIDs\n" +      "Name of the Segment List\n") +{ +	nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str); +	nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name); +	nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local"); +	nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config"); +	nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "explicit"); +	nb_cli_enqueue_change(vty, "./segment-list-name", NB_OP_MODIFY, +			      list_name); +	return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']", +				    preference_str); +} + +DEFPY_NOSH( +	srte_policy_candidate_dyn, +	srte_policy_candidate_dyn_cmd, +	"candidate-path preference (0-4294967295)$preference name WORD$name dynamic", +	"Segment Routing Policy Candidate Path\n" +	"Segment Routing Policy Candidate Path Preference\n" +	"Administrative Preference\n" +	"Segment Routing Policy Candidate Path Name\n" +	"Symbolic Name\n" +	"Dynamic Path\n") +{ +	char xpath[XPATH_CANDIDATE_BASELEN]; +	int ret; + +	snprintf(xpath, sizeof(xpath), "%s/candidate-path[preference='%s']", +		 VTY_CURR_XPATH, preference_str); + +	nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str); +	nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name); +	nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local"); +	nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config"); +	nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "dynamic"); +	ret = nb_cli_apply_changes(vty, "./candidate-path[preference='%s']", +				   preference_str); + +	if (ret == CMD_SUCCESS) +		VTY_PUSH_XPATH(SR_CANDIDATE_DYN_NODE, xpath); + +	return ret; +} + +DEFPY(srte_candidate_bandwidth, +      srte_candidate_bandwidth_cmd, +      "bandwidth BANDWIDTH$value [required$required]", +      "Define a bandwidth constraint\n" +      "Bandwidth value\n" +      "Required constraint\n") +{ +	nb_cli_enqueue_change(vty, "./constraints/bandwidth/required", +			      NB_OP_MODIFY, required ? "true" : "false"); +	nb_cli_enqueue_change(vty, "./constraints/bandwidth/value", +	                      NB_OP_MODIFY, value); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_no_bandwidth, +      srte_candidate_no_bandwidth_cmd, +      "no bandwidth [BANDWIDTH$value] [required$required]", +      NO_STR +      "Remove a bandwidth constraint\n" +      "Bandwidth value\n" +      "Required constraint\n") +{ +	nb_cli_enqueue_change(vty, "./constraints/bandwidth", NB_OP_DESTROY, +	                      NULL); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_affinity_filter, +      srte_candidate_affinity_filter_cmd, +      "affinity {exclude-any|include-any|include-all}$type BITPATTERN$value", +      "Affinity constraint\n" +      "Exclude any matching link\n" +      "Include any matching link\n" +      "Include all matching links\n" +      "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n") +{ +	uint32_t filter; +	char xpath[XPATH_CANDIDATE_MAXLEN]; +	char decimal_value[11]; + +	if (sscanf(value, "0x%x", &filter) != 1) { +		vty_out(vty, "affinity type: fscanf: %s\n", +			safe_strerror(errno)); +		return CMD_WARNING_CONFIG_FAILED; +	} +	snprintf(decimal_value, sizeof(decimal_value), "%u", filter); +	snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, decimal_value); + +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_no_affinity_filter, +      srte_candidate_no_affinity_filter_cmd, +      "no affinity {exclude-any|include-any|include-all}$type [BITPATTERN$value]", +      NO_STR +      "Affinity constraint\n" +      "Exclude any matching link\n" +      "Include any matching link\n" +      "Include all matching links\n" +      "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n") +{ +	char xpath[XPATH_CANDIDATE_MAXLEN]; + +	snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_metric, +      srte_candidate_metric_cmd, +      "metric [bound$bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type METRIC$value [required$required]", +      "Define a metric constraint\n" +      "If the metric is bounded\n" +      "IGP metric\n" +      "TE metric\n" +      "Hop Counts\n" +      "Aggregate bandwidth consumption\n" +      "Load of the most loaded link\n" +      "Cumulative IGP cost\n" +      "Cumulative TE cost\n" +      "P2MP IGP metric\n" +      "P2MP TE metric\n" +      "P2MP hop count metric\n" +      "Segment-ID (SID) Depth.\n" +      "Path Delay metric\n" +      "Path Delay Variation metric\n" +      "Path Loss metric\n" +      "P2MP Path Delay metric\n" +      "P2MP Path Delay variation metric\n" +      "P2MP Path Loss metric\n" +      "Number of adaptations on a path\n" +      "Number of layers on a path\n" +      "Domain Count metric\n" +      "Border Node Count metric\n" +      "Metric value\n" +      "Required constraint\n") +{ +	char xpath[XPATH_CANDIDATE_MAXLEN]; +	snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']/value", +	         type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, value); +	snprintf(xpath, sizeof(xpath), +	         "./constraints/metrics[type='%s']/is-bound", type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, +			      (bound != NULL) ? "true" : "false"); +	snprintf(xpath, sizeof(xpath), +	         "./constraints/metrics[type='%s']/required", type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, +			      required ? "true" : "false"); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_no_metric, +      srte_candidate_no_metric_cmd, +      "no metric [bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type [METRIC$value] [required$required]", +      NO_STR +      "Remove a metric constraint\n" +      "If the metric is bounded\n" +      "IGP metric\n" +      "TE metric\n" +      "Hop Counts\n" +      "Aggregate bandwidth consumption\n" +      "Load of the most loaded link\n" +      "Cumulative IGP cost\n" +      "Cumulative TE cost\n" +      "P2MP IGP metric\n" +      "P2MP TE metric\n" +      "P2MP hop count metric\n" +      "Segment-ID (SID) Depth.\n" +      "Path Delay metric\n" +      "Path Delay Variation metric\n" +      "Path Loss metric\n" +      "P2MP Path Delay metric\n" +      "P2MP Path Delay variation metric\n" +      "P2MP Path Loss metric\n" +      "Number of adaptations on a path\n" +      "Number of layers on a path\n" +      "Domain Count metric\n" +      "Border Node Count metric\n" +      "Metric value\n" +      "Required constraint\n") +{ +	char xpath[XPATH_CANDIDATE_MAXLEN]; +	snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']", +	         type); +	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_policy_no_candidate, +      srte_policy_no_candidate_cmd, +      "no candidate-path\ +	preference (0-4294967295)$preference\ +	[name WORD\ +	<\ +	  explicit segment-list WORD\ +	  |dynamic\ +	>]", +      NO_STR +      "Segment Routing Policy Candidate Path\n" +      "Segment Routing Policy Candidate Path Preference\n" +      "Administrative Preference\n" +      "Segment Routing Policy Candidate Path Name\n" +      "Symbolic Name\n" +      "Explicit Path\n" +      "List of SIDs\n" +      "Name of the Segment List\n" +      "Dynamic Path\n") +{ +	nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + +	return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']", +				    preference_str); +} + +DEFPY(srte_candidate_objfun, +      srte_candidate_objfun_cmd, +      "objective-function <mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>$type [required$required]", +      "Define an objective function constraint\n" +      "Minimum Cost Path\n" +      "Minimum Load Path\n" +      "Maximum residual Bandwidth Path\n" +      "Minimize aggregate Bandwidth Consumption\n" +      "Minimize the Load of the most loaded Link\n" +      "Minimize the Cumulative Cost of a set of paths\n" +      "Shortest Path Tree\n" +      "Minimum Cost Tree\n" +      "Minimum Packet Loss Path\n" +      "Maximum Under-Utilized Path\n" +      "Maximum Reserved Under-Utilized Path\n" +      "Minimize the number of Transit Domains\n" +      "Minimize the number of Border Nodes\n" +      "Minimize the number of Common Transit Domains\n" +      "Minimize the number of Shared Links\n" +      "Minimize the number of Shared SRLGs\n" +      "Minimize the number of Shared Nodes\n" +      "Required constraint\n") +{ +	char xpath[XPATH_CANDIDATE_MAXLEN]; +	nb_cli_enqueue_change(vty, "./constraints/objective-function", +	                      NB_OP_DESTROY, NULL); +	snprintf(xpath, sizeof(xpath), +	         "./constraints/objective-function/required"); +	nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, +			      required ? "true" : "false"); +	nb_cli_enqueue_change(vty, "./constraints/objective-function/type", +	                      NB_OP_MODIFY, type); +	return nb_cli_apply_changes(vty, NULL); +} + +DEFPY(srte_candidate_no_objfun, +      srte_candidate_no_objfun_cmd, +      "no objective-function [<mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>] [required$required]", +      NO_STR +      "Remove an objective function constraint\n" +      "Minimum Cost Path\n" +      "Minimum Load Path\n" +      "Maximum residual Bandwidth Path\n" +      "Minimize aggregate Bandwidth Consumption\n" +      "Minimize the Load of the most loaded Link\n" +      "Minimize the Cumulative Cost of a set of paths\n" +      "Shortest Path Tree\n" +      "Minimum Cost Tree\n" +      "Minimum Packet Loss Path\n" +      "Maximum Under-Utilized Path\n" +      "Maximum Reserved Under-Utilized Path\n" +      "Minimize the number of Transit Domains\n" +      "Minimize the number of Border Nodes\n" +      "Minimize the number of Common Transit Domains\n" +      "Minimize the number of Shared Links\n" +      "Minimize the number of Shared SRLGs\n" +      "Minimize the number of Shared Nodes\n" +      "Required constraint\n") +{ +	nb_cli_enqueue_change(vty, "./constraints/objective-function", +	                      NB_OP_DESTROY, NULL); +	return nb_cli_apply_changes(vty, NULL); +} + +static const char *objfun_type_name(enum objfun_type type) +{ +	switch (type) { +	case OBJFUN_MCP: +		return "mcp"; +	case OBJFUN_MLP: +		return "mlp"; +	case OBJFUN_MBP: +		return "mbp"; +	case OBJFUN_MBC: +		return "mbc"; +	case OBJFUN_MLL: +		return "mll"; +	case OBJFUN_MCC: +		return "mcc"; +	case OBJFUN_SPT: +		return "spt"; +	case OBJFUN_MCT: +		return "mct"; +	case OBJFUN_MPLP: +		return "mplp"; +	case OBJFUN_MUP: +		return "mup"; +	case OBJFUN_MRUP: +		return "mrup"; +	case OBJFUN_MTD: +		return "mtd"; +	case OBJFUN_MBN: +		return "mbn"; +	case OBJFUN_MCTD: +		return "mctd"; +	case OBJFUN_MSL: +		return "msl"; +	case OBJFUN_MSS: +		return "mss"; +	case OBJFUN_MSN: +		return "msn"; +	default: +		return NULL; +	} +} + +DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd, +	   "show debugging [pathd]", +	   SHOW_STR +	   "State of each debugging option\n" +	   "pathd module debugging\n") +{ +	/* nothing to do here */ +	return CMD_SUCCESS; +} + +static const char *metric_type_name(enum srte_candidate_metric_type type) +{ +	switch (type) { +	case SRTE_CANDIDATE_METRIC_TYPE_IGP: +		return "igp"; +	case SRTE_CANDIDATE_METRIC_TYPE_TE: +		return "te"; +	case SRTE_CANDIDATE_METRIC_TYPE_HC: +		return "hc"; +	case SRTE_CANDIDATE_METRIC_TYPE_ABC: +		return "abc"; +	case SRTE_CANDIDATE_METRIC_TYPE_LMLL: +		return "lmll"; +	case SRTE_CANDIDATE_METRIC_TYPE_CIGP: +		return "cigp"; +	case SRTE_CANDIDATE_METRIC_TYPE_CTE: +		return "cte"; +	case SRTE_CANDIDATE_METRIC_TYPE_PIGP: +		return "pigp"; +	case SRTE_CANDIDATE_METRIC_TYPE_PTE: +		return "pte"; +	case SRTE_CANDIDATE_METRIC_TYPE_PHC: +		return "phc"; +	case SRTE_CANDIDATE_METRIC_TYPE_MSD: +		return "msd"; +	case SRTE_CANDIDATE_METRIC_TYPE_PD: +		return "pd"; +	case SRTE_CANDIDATE_METRIC_TYPE_PDV: +		return "pdv"; +	case SRTE_CANDIDATE_METRIC_TYPE_PL: +		return "pl"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPD: +		return "ppd"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPDV: +		return "ppdv"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPL: +		return "ppl"; +	case SRTE_CANDIDATE_METRIC_TYPE_NAP: +		return "nap"; +	case SRTE_CANDIDATE_METRIC_TYPE_NLP: +		return "nlp"; +	case SRTE_CANDIDATE_METRIC_TYPE_DC: +		return "dc"; +	case SRTE_CANDIDATE_METRIC_TYPE_BNC: +		return "bnc"; +	default: +		return NULL; +	} +} + +static void config_write_float(struct vty *vty, float value) +{ +	if (fabs(truncf(value) - value) < FLT_EPSILON) { +		vty_out(vty, " %d", (int)value); +		return; +	} else { +		vty_out(vty, " %f", value); +	} +} + +static void config_write_metric(struct vty *vty, +				enum srte_candidate_metric_type type, +				float value, bool required, bool is_bound) +{ +	const char *name = metric_type_name(type); +	if (name == NULL) +		return; +	vty_out(vty, "    metric %s%s", is_bound ? "bound " : "", +	        metric_type_name(type)); +	config_write_float(vty, value); +	vty_out(vty, required ? " required" : ""); +	vty_out(vty, "\n"); +} + +static int config_write_metric_cb(const struct lyd_node *dnode, void *arg) +{ +	struct vty *vty = arg; +	enum srte_candidate_metric_type type; +	bool required, is_bound = false; +	float value; + +	type = yang_dnode_get_enum(dnode, "./type"); +	value = (float)yang_dnode_get_dec64(dnode, "./value"); +	required = yang_dnode_get_bool(dnode, "./required"); +	if (yang_dnode_exists(dnode, "./is-bound")) +		is_bound = yang_dnode_get_bool(dnode, "./is-bound"); + +	config_write_metric(vty, type, value, required, is_bound); +	return YANG_ITER_CONTINUE; +} + +void cli_show_srte_policy_candidate_path(struct vty *vty, +					 struct lyd_node *dnode, +					 bool show_defaults) +{ +	float bandwidth; +	uint32_t affinity; +	bool required; +	enum objfun_type objfun_type; +	const char *type = yang_dnode_get_string(dnode, "./type"); + +	vty_out(vty, "   candidate-path preference %s name %s %s", +		yang_dnode_get_string(dnode, "./preference"), +		yang_dnode_get_string(dnode, "./name"), type); +	if (strmatch(type, "explicit")) +		vty_out(vty, " segment-list %s", +			yang_dnode_get_string(dnode, "./segment-list-name")); +	vty_out(vty, "\n"); + +	if (strmatch(type, "dynamic")) { +		if (yang_dnode_exists(dnode, "./constraints/bandwidth")) { +			bandwidth = (float)yang_dnode_get_dec64( +				dnode, "./constraints/bandwidth/value"); +			required = yang_dnode_get_bool( +				dnode, "./constraints/bandwidth/required"); +			vty_out(vty, "    %sbandwidth", +				required ? "required " : ""); +			config_write_float(vty, bandwidth); +			vty_out(vty, "\n"); +		} +		if (yang_dnode_exists(dnode, +				      "./constraints/affinity/exclude-any")) { +			affinity = yang_dnode_get_uint32( +				dnode, "./constraints/affinity/exclude-any"); +			vty_out(vty, "    affinity exclude-any 0x%08x\n", +				affinity); +		} +		if (yang_dnode_exists(dnode, +				      "./constraints/affinity/include-any")) { +			affinity = yang_dnode_get_uint32( +				dnode, "./constraints/affinity/include-any"); +			vty_out(vty, "    affinity include-any 0x%08x\n", +				affinity); +		} +		if (yang_dnode_exists(dnode, +				      "./constraints/affinity/include-all")) { +			affinity = yang_dnode_get_uint32( +				dnode, "./constraints/affinity/include-all"); +			vty_out(vty, "    affinity include-all 0x%08x\n", +				affinity); +		} +		yang_dnode_iterate(config_write_metric_cb, vty, dnode, +				   "./constraints/metrics"); +		if (yang_dnode_exists(dnode, +		                      "./constraints/objective-function")) { +			objfun_type = yang_dnode_get_enum(dnode, +				"./constraints/objective-function/type"); +			required = yang_dnode_get_bool(dnode, +				"./constraints/objective-function/required"); +			vty_out(vty, "    objective-function %s%s\n", +			        objfun_type_name(objfun_type), +				required ? " required" : ""); +		} +	} +} + +static int config_write_dnode(const struct lyd_node *dnode, void *arg) +{ +	struct vty *vty = arg; + +	nb_cli_show_dnode_cmds(vty, (struct lyd_node *)dnode, false); + +	return YANG_ITER_CONTINUE; +} + +int config_write_segment_routing(struct vty *vty) +{ +	vty_out(vty, "segment-routing\n"); +	return 1; +} + +int config_write_traffic_eng(struct vty *vty) +{ +	vty_out(vty, " traffic-eng\n"); +	return 1; +} + +int config_write_segment_lists(struct vty *vty) +{ +	yang_dnode_iterate(config_write_dnode, vty, running_config->dnode, +			   "/frr-pathd:pathd/srte/segment-list"); + +	return 1; +} + +int config_write_sr_policies(struct vty *vty) +{ +	yang_dnode_iterate(config_write_dnode, vty, running_config->dnode, +			   "/frr-pathd:pathd/srte/policy"); + +	return 1; +} + +void path_cli_init(void) +{ +	install_node(&segment_routing_node); +	install_node(&sr_traffic_eng_node); +	install_node(&srte_segment_list_node); +	install_node(&srte_policy_node); +	install_node(&srte_candidate_dyn_node); +	install_default(SEGMENT_ROUTING_NODE); +	install_default(SR_TRAFFIC_ENG_NODE); +	install_default(SR_SEGMENT_LIST_NODE); +	install_default(SR_POLICY_NODE); +	install_default(SR_CANDIDATE_DYN_NODE); + +	install_element(ENABLE_NODE, &show_debugging_pathd_cmd); +	install_element(ENABLE_NODE, &show_srte_policy_cmd); +	install_element(ENABLE_NODE, &show_srte_policy_detail_cmd); + +	install_element(CONFIG_NODE, &segment_routing_cmd); +	install_element(SEGMENT_ROUTING_NODE, &sr_traffic_eng_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &srte_segment_list_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &srte_no_segment_list_cmd); +	install_element(SR_SEGMENT_LIST_NODE, +			&srte_segment_list_segment_cmd); +	install_element(SR_SEGMENT_LIST_NODE, +			&srte_segment_list_no_segment_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &srte_no_policy_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_name_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_no_name_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_binding_sid_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_no_binding_sid_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_candidate_exp_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_candidate_dyn_cmd); +	install_element(SR_POLICY_NODE, &srte_policy_no_candidate_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_bandwidth_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_no_bandwidth_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_affinity_filter_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_no_affinity_filter_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_metric_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_no_metric_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_objfun_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, +			&srte_candidate_no_objfun_cmd); +} diff --git a/pathd/path_debug.c b/pathd/path_debug.c new file mode 100644 index 0000000000..df0550715a --- /dev/null +++ b/pathd/path_debug.c @@ -0,0 +1,121 @@ +/* + * 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_debug.h" + +THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE]; + +/** + * Gives the string representation of an srte_protocol_origin enum value. + * + * @param origin The enum value to convert to string + * @return a constant string representation of the enum value + */ +const char *srte_protocol_origin_name(enum srte_protocol_origin origin) +{ +	switch (origin) { +	case SRTE_ORIGIN_UNDEFINED: +		return "UNDEFINED"; +	case SRTE_ORIGIN_PCEP: +		return "PCEP"; +	case SRTE_ORIGIN_BGP: +		return "BGP"; +	case SRTE_ORIGIN_LOCAL: +		return "LOCAL"; +	default: +		return "UNKNOWN"; +	} +} + +/** + * Gives the string representation of an srte_candidate_type enum value. + * + * @param origin The enum value to convert to string + * @return a constant string representation of the enum value + */ +const char *srte_candidate_type_name(enum srte_candidate_type type) +{ +	switch (type) { +	case SRTE_CANDIDATE_TYPE_EXPLICIT: +		return "EXPLICIT"; +	case SRTE_CANDIDATE_TYPE_DYNAMIC: +		return "DYNAMIC"; +	case SRTE_CANDIDATE_TYPE_UNDEFINED: +		return "UNDEFINED"; +	default: +		return "UNKNOWN"; +	} +} + +/** + * Gives the string representation of an objfun_type enum value. + * + * @param origin The enum value to convert to string + * @return a constant string representation of the enum value + */ +const char *objfun_type_name(enum objfun_type type) +{ +	switch (type) { +	case OBJFUN_UNDEFINED: +		return "UNDEFINED"; +	case OBJFUN_MCP: +		return "MCP"; +	case OBJFUN_MLP: +		return "MLP"; +	case OBJFUN_MBP: +		return "MBP"; +	case OBJFUN_MBC: +		return "MBC"; +	case OBJFUN_MLL: +		return "MLL"; +	case OBJFUN_MCC: +		return "MCC"; +	case OBJFUN_SPT: +		return "SPT"; +	case OBJFUN_MCT: +		return "MCT"; +	case OBJFUN_MPLP: +		return "MPLP"; +	case OBJFUN_MUP: +		return "MUP"; +	case OBJFUN_MRUP: +		return "MRUP"; +	case OBJFUN_MTD: +		return "MTD"; +	case OBJFUN_MBN: +		return "MBN"; +	case OBJFUN_MCTD: +		return "MCTD"; +	case OBJFUN_MSL: +		return "MSL"; +	case OBJFUN_MSS: +		return "MSS"; +	case OBJFUN_MSN: +		return "MSN"; +	default: +		return "UNKNOWN"; +	} +} diff --git a/pathd/path_debug.h b/pathd/path_debug.h new file mode 100644 index 0000000000..d9cfcb6ba2 --- /dev/null +++ b/pathd/path_debug.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_DEBUG_H_ +#define _PATH_DEBUG_H_ + +#include "pathd/pathd.h" + +#ifdef __GNUC__ +#define THREAD_DATA __thread +#else +#define THREAD_DATA +#endif + +#define DEBUG_IDENT_SIZE 4 +#define DEBUG_BUFF_SIZE 4096 +#define TUP(A, B) ((((uint32_t)(A)) << 16) | ((uint32_t)(B))) +#define PATHD_FORMAT_INIT() _debug_buff[0] = 0 +#define PATHD_FORMAT(fmt, ...)                                                 \ +	csnprintfrr(_debug_buff, DEBUG_BUFF_SIZE, fmt, ##__VA_ARGS__) +#define PATHD_FORMAT_FINI() _debug_buff + +extern THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE]; + +const char *srte_protocol_origin_name(enum srte_protocol_origin origin); +const char *srte_candidate_type_name(enum srte_candidate_type type); +const char *objfun_type_name(enum objfun_type type); + +#endif // _PATH_DEBUG_H_
\ No newline at end of file diff --git a/pathd/path_errors.c b/pathd/path_errors.c new file mode 100644 index 0000000000..f266b6b4ea --- /dev/null +++ b/pathd/path_errors.c @@ -0,0 +1,43 @@ +/* + * pathd-specific error messages. + * 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 "lib/ferr.h" +#include "path_errors.h" + +/* clang-format off */ +static struct log_ref ferr_path_err[] = { +	{ +		.code = END_FERR, +	} +}; + +static struct log_ref ferr_path_warn[] = { +	{ +		.code = END_FERR, +	} +}; +/* clang-format on */ + +void path_error_init(void) +{ +	log_ref_add(ferr_path_err); +	log_ref_add(ferr_path_warn); +} diff --git a/pathd/path_errors.h b/pathd/path_errors.h new file mode 100644 index 0000000000..366878f300 --- /dev/null +++ b/pathd/path_errors.h @@ -0,0 +1,30 @@ +/* + * 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_ERRORS_H__ +#define __PATH_ERRORS_H__ + +#include "lib/ferr.h" + +enum path_log_refs { +	EC_PATH_PCEP_INIT = PATH_FERR_START, +}; + +extern void path_error_init(void); + +#endif diff --git a/pathd/path_main.c b/pathd/path_main.c new file mode 100644 index 0000000000..8b7d4aba48 --- /dev/null +++ b/pathd/path_main.c @@ -0,0 +1,154 @@ +/* + * 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 <lib/version.h> +#include "getopt.h" +#include "thread.h" +#include "command.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "libfrr.h" +#include "vrf.h" +#include "filter.h" + +#include "pathd.h" +#include "path_nb.h" +#include "path_zebra.h" +#include "path_errors.h" + +char backup_config_file[256]; + +zebra_capabilities_t _caps_p[] = {}; + +struct zebra_privs_t pathd_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) +	.user = FRR_USER, +	.group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) +	.vty_group = VTY_GROUP, +#endif +	.caps_p = _caps_p, +	.cap_num_p = array_size(_caps_p), +	.cap_num_i = 0}; + +struct option longopts[] = {{0}}; + +/* Master of threads. */ +struct thread_master *master; + +static struct frr_daemon_info pathd_di; + +/* SIGHUP handler. */ +static void sighup(void) +{ +	zlog_info("SIGHUP received"); + +	/* Reload config file. */ +	vty_read_config(NULL, pathd_di.config_file, config_default); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ +	zlog_notice("Terminating on signal"); + +	exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ +	zlog_rotate(); +} + +struct quagga_signal_t path_signals[] = { +	{ +		.signal = SIGHUP, +		.handler = &sighup, +	}, +	{ +		.signal = SIGUSR1, +		.handler = &sigusr1, +	}, +	{ +		.signal = SIGINT, +		.handler = &sigint, +	}, +	{ +		.signal = SIGTERM, +		.handler = &sigint, +	}, +}; + +static const struct frr_yang_module_info *pathd_yang_modules[] = { +	&frr_filter_info, +	&frr_interface_info, +	&frr_pathd_info, +}; + +#define PATH_VTY_PORT 2621 + +FRR_DAEMON_INFO(pathd, PATH, .vty_port = PATH_VTY_PORT, + +		.proghelp = "Implementation of PATH.", + +		.signals = path_signals, .n_signals = array_size(path_signals), + +		.privs = &pathd_privs, .yang_modules = pathd_yang_modules, +		.n_yang_modules = array_size(pathd_yang_modules), ) + +int main(int argc, char **argv, char **envp) +{ +	frr_preinit(&pathd_di, argc, argv); +	frr_opt_add("", longopts, ""); + +	while (1) { +		int opt; + +		opt = frr_getopt(argc, argv, NULL); + +		if (opt == EOF) +			break; + +		switch (opt) { +		case 0: +			break; +		default: +			frr_help_exit(1); +			break; +		} +	} + +	master = frr_init(); + +	access_list_init(); + +	path_error_init(); +	path_zebra_init(master); +	path_cli_init(); + +	frr_config_fork(); +	frr_run(master); + +	/* Not reached. */ +	return 0; +} diff --git a/pathd/path_memory.c b/pathd/path_memory.c new file mode 100644 index 0000000000..ad4904a298 --- /dev/null +++ b/pathd/path_memory.c @@ -0,0 +1,25 @@ +/* + * 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_memory.h" + +DEFINE_MGROUP(PATHD, "pathd") diff --git a/pathd/path_memory.h b/pathd/path_memory.h new file mode 100644 index 0000000000..e2f6787f66 --- /dev/null +++ b/pathd/path_memory.h @@ -0,0 +1,26 @@ +/* + * 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_MEMORY_H_ +#define _FRR_PATH_MEMORY_H_ + +#include "memory.h" + +DECLARE_MGROUP(PATHD) + +#endif /* _FRR_PATH_MEMORY_H_ */ diff --git a/pathd/path_nb.c b/pathd/path_nb.c new file mode 100644 index 0000000000..a210e31b9c --- /dev/null +++ b/pathd/path_nb.c @@ -0,0 +1,351 @@ +/* + * 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 "northbound.h" +#include "libfrr.h" + +#include "pathd/path_nb.h" + +static int iter_objfun_cb(const struct lyd_node *dnode, void *arg); +static int dummy_create(struct nb_cb_create_args *args); +static int dummy_modify(struct nb_cb_modify_args *args); +static int dummy_destroy(struct nb_cb_destroy_args *args); + +struct of_cb_pref { +	uint32_t index; +	enum objfun_type type; +	struct of_cb_pref *next; +}; + +struct of_cb_args { +	struct of_cb_pref *first; +	uint32_t free_slot; +	struct of_cb_pref prefs[MAX_OBJFUN_TYPE]; +}; + +/* clang-format off */ +const struct frr_yang_module_info frr_pathd_info = { +	.name = "frr-pathd", +	.nodes = { +		{ +			.xpath = "/frr-pathd:pathd", +			.cbs = { +				.apply_finish = pathd_apply_finish, +			}, +			.priority = NB_DFLT_PRIORITY + 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list", +			.cbs = { +				.create = pathd_srte_segment_list_create, +				.cli_show = cli_show_srte_segment_list, +				.destroy = pathd_srte_segment_list_destroy, +				.get_next = pathd_srte_segment_list_get_next, +				.get_keys = pathd_srte_segment_list_get_keys, +				.lookup_entry = pathd_srte_segment_list_lookup_entry, +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/protocol-origin", +			.cbs = { +				.modify = pathd_srte_segment_list_protocol_origin_modify, +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/originator", +			.cbs = { +				.modify = pathd_srte_segment_list_originator_modify, +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment", +			.cbs = { +				.create = pathd_srte_segment_list_segment_create, +				.cli_show = cli_show_srte_segment_list_segment, +				.destroy = pathd_srte_segment_list_segment_destroy, +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/sid-value", +			.cbs = { +				.modify = pathd_srte_segment_list_segment_sid_value_modify, +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai", +			.cbs = { +				.create = dummy_create, +				.destroy = pathd_srte_segment_list_segment_nai_destroy, +				.apply_finish = pathd_srte_segment_list_segment_nai_apply_finish +			}, +			.priority = NB_DFLT_PRIORITY - 1 +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/type", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-address", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-interface", +			.cbs = {.modify = dummy_modify, .destroy = dummy_destroy} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-address", +			.cbs = {.modify = dummy_modify, .destroy = dummy_destroy} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-interface", +			.cbs = {.modify = dummy_modify, .destroy = dummy_destroy} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy", +			.cbs = { +				.create = pathd_srte_policy_create, +				.cli_show = cli_show_srte_policy, +				.destroy = pathd_srte_policy_destroy, +				.get_next = pathd_srte_policy_get_next, +				.get_keys = pathd_srte_policy_get_keys, +				.lookup_entry = pathd_srte_policy_lookup_entry, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/name", +			.cbs = { +				.modify = pathd_srte_policy_name_modify, +				.cli_show = cli_show_srte_policy_name, +				.destroy = pathd_srte_policy_name_destroy, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/binding-sid", +			.cbs = { +				.modify = pathd_srte_policy_binding_sid_modify, +				.cli_show = cli_show_srte_policy_binding_sid, +				.destroy = pathd_srte_policy_binding_sid_destroy, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/is-operational", +			.cbs = { +				.get_elem = pathd_srte_policy_is_operational_get_elem +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path", +			.cbs = { +				.create = pathd_srte_policy_candidate_path_create, +				.cli_show = cli_show_srte_policy_candidate_path, +				.destroy = pathd_srte_policy_candidate_path_destroy, +				.get_next = pathd_srte_policy_candidate_path_get_next, +				.get_keys = pathd_srte_policy_candidate_path_get_keys, +				.lookup_entry = pathd_srte_policy_candidate_path_lookup_entry, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/name", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_name_modify, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/is-best-candidate-path", +			.cbs = { +				.get_elem = pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/protocol-origin", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_protocol_origin_modify, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/originator", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_originator_modify, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/discriminator", +			.cbs = { +				.get_elem = pathd_srte_policy_candidate_path_discriminator_get_elem, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/type", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_type_modify, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/segment-list-name", +			.cbs = { +				.destroy = pathd_srte_policy_candidate_path_segment_list_name_destroy, +				.modify = pathd_srte_policy_candidate_path_segment_list_name_modify, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth", +			.cbs = { +				.create = dummy_create, +				.destroy = pathd_srte_policy_candidate_path_bandwidth_destroy, +				.apply_finish = pathd_srte_policy_candidate_path_bandwidth_apply_finish +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/required", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/value", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_exclude_any_modify, +				.destroy = pathd_srte_policy_candidate_path_exclude_any_destroy, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_include_any_modify, +				.destroy = pathd_srte_policy_candidate_path_include_any_destroy, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all", +			.cbs = { +				.modify = pathd_srte_policy_candidate_path_include_all_modify, +				.destroy = pathd_srte_policy_candidate_path_include_all_destroy, +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics", +			.cbs = { +				.create = dummy_create, +				.destroy = pathd_srte_policy_candidate_path_metrics_destroy, +				.apply_finish = pathd_srte_policy_candidate_path_metrics_apply_finish +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/value", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/required", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-bound", +			.cbs = {.modify = dummy_modify, .destroy = dummy_destroy} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-computed", +			.cbs = {.modify = dummy_modify, .destroy = dummy_destroy} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function", +			.cbs = { +				.create = dummy_create, +				.destroy = pathd_srte_policy_candidate_path_objfun_destroy, +				.apply_finish = pathd_srte_policy_candidate_path_objfun_apply_finish +			} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/required", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/type", +			.cbs = {.modify = dummy_modify} +		}, +		{ +			.xpath = NULL, +		}, +	} +}; + +void iter_objfun_prefs(const struct lyd_node *dnode, const char* path, +		       of_pref_cp_t fun, void *arg) +{ +	struct of_cb_args args = {0}; +	struct of_cb_pref *p; + +	yang_dnode_iterate(iter_objfun_cb, &args, dnode, path); +	for (p = args.first; p != NULL; p = p->next) +		fun(p->type, arg); +} + +int iter_objfun_cb(const struct lyd_node *dnode, void *arg) +{ +	struct of_cb_args *of_arg = arg; +	struct of_cb_pref *pref; +	struct of_cb_pref **p; + +	if (of_arg->free_slot >= MAX_OBJFUN_TYPE) +		return YANG_ITER_STOP; + +	pref = &of_arg->prefs[of_arg->free_slot++]; + +	pref->index = yang_dnode_get_uint32(dnode, "./index"); +	pref->type = yang_dnode_get_enum(dnode, "./type"); + +	/* Simplistic insertion sort */ +	p = &of_arg->first; +	while (true) { +		if (*p == NULL) { +			*p = pref; +			break; +		} +		if ((*p)->index >= pref->index) { +			pref->next = *p; +			*p = pref; +			break; +		} +		p = &(*p)->next; +	} + +	return YANG_ITER_CONTINUE; +} + +int dummy_create(struct nb_cb_create_args *args) +{ +	return NB_OK; +} + +int dummy_modify(struct nb_cb_modify_args *args) +{ +	return NB_OK; +} + +int dummy_destroy(struct nb_cb_destroy_args *args) +{ +	return NB_OK; +} diff --git a/pathd/path_nb.h b/pathd/path_nb.h new file mode 100644 index 0000000000..3a0b3863ce --- /dev/null +++ b/pathd/path_nb.h @@ -0,0 +1,130 @@ +/* + * 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_NB_H_ +#define _FRR_PATH_NB_H_ + +#include "pathd/pathd.h" + +extern const struct frr_yang_module_info frr_pathd_info; + +/* Mandatory callbacks. */ +int pathd_srte_segment_list_create(struct nb_cb_create_args *args); +int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args); + +const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args); +int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args); +const void * +pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args); + +int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args); +int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args); +int pathd_srte_segment_list_protocol_origin_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args); +int pathd_srte_segment_list_segment_sid_value_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_segment_list_segment_nai_destroy( +	struct nb_cb_destroy_args *args); +void pathd_srte_segment_list_segment_nai_apply_finish( +	struct nb_cb_apply_finish_args *args); +int pathd_srte_policy_create(struct nb_cb_create_args *args); +int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args); +const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args); +int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args); +const void * +pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args); +int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args); +int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args); +int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args); +int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args); +struct yang_data * +pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args); +int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args); +int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args); +int pathd_srte_policy_candidate_path_name_modify( +	struct nb_cb_modify_args *args); +const void * +pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args); +int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args); +const void *pathd_srte_policy_candidate_path_lookup_entry( +	struct nb_cb_lookup_entry_args *args); +void pathd_srte_policy_candidate_path_bandwidth_apply_finish( +	struct nb_cb_apply_finish_args *args); +int pathd_srte_policy_candidate_path_bandwidth_destroy( +	struct nb_cb_destroy_args *args); +int pathd_srte_policy_candidate_path_exclude_any_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_exclude_any_destroy( +	struct nb_cb_destroy_args *args); +int pathd_srte_policy_candidate_path_include_any_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_include_any_destroy( +	struct nb_cb_destroy_args *args); +int pathd_srte_policy_candidate_path_include_all_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_include_all_destroy( +	struct nb_cb_destroy_args *args); +int pathd_srte_policy_candidate_path_metrics_destroy( +	struct nb_cb_destroy_args *args); +void pathd_srte_policy_candidate_path_metrics_apply_finish( +	struct nb_cb_apply_finish_args *args); +int pathd_srte_policy_candidate_path_objfun_destroy( +	struct nb_cb_destroy_args *args); +void pathd_srte_policy_candidate_path_objfun_apply_finish( +	struct nb_cb_apply_finish_args *args); +struct yang_data * +pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem( +	struct nb_cb_get_elem_args *args); +int pathd_srte_policy_candidate_path_protocol_origin_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_originator_modify( +	struct nb_cb_modify_args *args); +struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem( +	struct nb_cb_get_elem_args *args); +int pathd_srte_policy_candidate_path_type_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_segment_list_name_modify( +	struct nb_cb_modify_args *args); +int pathd_srte_policy_candidate_path_segment_list_name_destroy( +	struct nb_cb_destroy_args *args); + +/* Optional 'apply_finish' callbacks. */ +void pathd_apply_finish(struct nb_cb_apply_finish_args *args); + +/* Optional 'cli_show' callbacks. */ +void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode, +				bool show_defaults); +void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode, +					bool show_defaults); +void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode, +			  bool show_defaults); +void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode, +			       bool show_defaults); +void cli_show_srte_policy_binding_sid(struct vty *vty, struct lyd_node *dnode, +				      bool show_defaults); +void cli_show_srte_policy_candidate_path(struct vty *vty, +					 struct lyd_node *dnode, +					 bool show_defaults); + +/* Utility functions */ +typedef void (*of_pref_cp_t)(enum objfun_type type, void *arg); +void iter_objfun_prefs(const struct lyd_node *dnode, const char *path, +		       of_pref_cp_t fun, void *arg); + +#endif /* _FRR_PATH_NB_H_ */ diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c new file mode 100644 index 0000000000..669db169ae --- /dev/null +++ b/pathd/path_nb_config.c @@ -0,0 +1,731 @@ +/* + * 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 <lib_errors.h> + +#include "northbound.h" +#include "libfrr.h" + +#include "pathd/path_zebra.h" +#include "pathd/path_nb.h" + +/* + * XPath: /frr-pathd:pathd + */ +void pathd_apply_finish(struct nb_cb_apply_finish_args *args) +{ +	srte_apply_changes(); +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list + */ +int pathd_srte_segment_list_create(struct nb_cb_create_args *args) +{ +	struct srte_segment_list *segment_list; +	const char *name; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	name = yang_dnode_get_string(args->dnode, "./name"); +	segment_list = srte_segment_list_add(name); +	nb_running_set_entry(args->dnode, segment_list); +	SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); + +	return NB_OK; +} + +int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_segment_list *segment_list; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment_list = nb_running_unset_entry(args->dnode); +	SET_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list/protocol-origin + */ +int pathd_srte_segment_list_protocol_origin_modify( +	struct nb_cb_modify_args *args) +{ +	struct srte_segment_list *segment_list; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment_list = nb_running_get_entry(args->dnode, NULL, true); +	segment_list->protocol_origin = yang_dnode_get_enum(args->dnode, NULL); +	SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list/originator + */ +int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args) +{ +	struct srte_segment_list *segment_list; +	const char *originator; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment_list = nb_running_get_entry(args->dnode, NULL, true); +	originator = yang_dnode_get_string(args->dnode, NULL); +	strlcpy(segment_list->originator, originator, +		sizeof(segment_list->originator)); +	SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + +	return NB_OK; +} + + +/* + * XPath: /frr-pathd:pathd/srte/segment-list/segment + */ +int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args) +{ +	struct srte_segment_list *segment_list; +	struct srte_segment_entry *segment; +	uint32_t index; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment_list = nb_running_get_entry(args->dnode, NULL, true); +	index = yang_dnode_get_uint32(args->dnode, "./index"); +	segment = srte_segment_entry_add(segment_list, index); +	nb_running_set_entry(args->dnode, segment); +	SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); + +	return NB_OK; +} + +int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_segment_entry *segment; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment = nb_running_unset_entry(args->dnode); +	SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + +	srte_segment_entry_del(segment); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/segment-list/segment/sid-value + */ +int pathd_srte_segment_list_segment_sid_value_modify( +	struct nb_cb_modify_args *args) +{ +	mpls_label_t sid_value; +	struct srte_segment_entry *segment; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment = nb_running_get_entry(args->dnode, NULL, true); +	sid_value = yang_dnode_get_uint32(args->dnode, NULL); +	segment->sid_value = sid_value; +	SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED); + +	return NB_OK; +} + +int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_segment_entry *segment; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	segment = nb_running_get_entry(args->dnode, NULL, true); +	segment->nai_type = SRTE_SEGMENT_NAI_TYPE_NONE; +	segment->nai_local_addr.ipa_type = IPADDR_NONE; +	segment->nai_local_iface = 0; +	segment->nai_remote_addr.ipa_type = IPADDR_NONE; +	segment->nai_remote_iface = 0; + +	return NB_OK; +} + +void pathd_srte_segment_list_segment_nai_apply_finish( +	struct nb_cb_apply_finish_args *args) +{ +	struct srte_segment_entry *segment; +	enum srte_segment_nai_type type; +	struct ipaddr local_addr, remote_addr; +	uint32_t local_iface = 0, remote_iface = 0; + +	segment = nb_running_get_entry(args->dnode, NULL, true); +	type = yang_dnode_get_enum(args->dnode, "./type"); + +	yang_dnode_get_ip(&local_addr, args->dnode, "./local-address"); + +	switch (type) { +	case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: +	case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: +		break; +	case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: +	case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: +		yang_dnode_get_ip(&remote_addr, args->dnode, +				  "./remote-address"); +		break; +	case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: +		yang_dnode_get_ip(&remote_addr, args->dnode, +				  "./remote-address"); +		local_iface = +			yang_dnode_get_uint32(args->dnode, "./local-interface"); +		remote_iface = yang_dnode_get_uint32(args->dnode, +						     "./remote-interface"); +		break; +	default: +		break; +	} + +	srte_segment_entry_set_nai(segment, type, &local_addr, local_iface, +				   &remote_addr, remote_iface); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy + */ +int pathd_srte_policy_create(struct nb_cb_create_args *args) +{ +	struct srte_policy *policy; +	uint32_t color; +	struct ipaddr endpoint; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	color = yang_dnode_get_uint32(args->dnode, "./color"); +	yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint"); +	policy = srte_policy_add(color, &endpoint); + +	nb_running_set_entry(args->dnode, policy); +	SET_FLAG(policy->flags, F_POLICY_NEW); + +	return NB_OK; +} + +int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_policy *policy; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	policy = nb_running_unset_entry(args->dnode); +	SET_FLAG(policy->flags, F_POLICY_DELETED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/name + */ +int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args) +{ +	struct srte_policy *policy; +	const char *name; + +	if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE) +		return NB_OK; + +	policy = nb_running_get_entry(args->dnode, NULL, true); + +	if (args->event == NB_EV_VALIDATE) { +		/* the policy name is fixed after setting it once */ +		if (strlen(policy->name) > 0) { +			flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, +				  "The SR Policy name is fixed!"); +			return NB_ERR_RESOURCE; +		} else +			return NB_OK; +	} + +	name = yang_dnode_get_string(args->dnode, NULL); +	strlcpy(policy->name, name, sizeof(policy->name)); +	SET_FLAG(policy->flags, F_POLICY_MODIFIED); + +	return NB_OK; +} + +int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_policy *policy; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	policy = nb_running_get_entry(args->dnode, NULL, true); +	policy->name[0] = '\0'; +	SET_FLAG(policy->flags, F_POLICY_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/binding-sid + */ +int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args) +{ +	struct srte_policy *policy; +	mpls_label_t binding_sid; + +	policy = nb_running_get_entry(args->dnode, NULL, true); +	binding_sid = yang_dnode_get_uint32(args->dnode, NULL); + +	switch (args->event) { +	case NB_EV_VALIDATE: +		break; +	case NB_EV_PREPARE: +		if (path_zebra_request_label(binding_sid) < 0) +			return NB_ERR_RESOURCE; +		break; +	case NB_EV_ABORT: +		break; +	case NB_EV_APPLY: +		srte_policy_update_binding_sid(policy, binding_sid); +		SET_FLAG(policy->flags, F_POLICY_MODIFIED); +		break; +	} + +	return NB_OK; +} + +int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_policy *policy; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	policy = nb_running_get_entry(args->dnode, NULL, true); +	srte_policy_update_binding_sid(policy, MPLS_LABEL_NONE); +	SET_FLAG(policy->flags, F_POLICY_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path + */ +int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args) +{ +	struct srte_policy *policy; +	struct srte_candidate *candidate; +	uint32_t preference; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	policy = nb_running_get_entry(args->dnode, NULL, true); +	preference = yang_dnode_get_uint32(args->dnode, "./preference"); +	candidate = srte_candidate_add(policy, preference); +	nb_running_set_entry(args->dnode, candidate); +	SET_FLAG(candidate->flags, F_CANDIDATE_NEW); + +	return NB_OK; +} + +int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args) +{ +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	candidate = nb_running_unset_entry(args->dnode); +	SET_FLAG(candidate->flags, F_CANDIDATE_DELETED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/name + */ +int pathd_srte_policy_candidate_path_name_modify(struct nb_cb_modify_args *args) +{ +	struct srte_candidate *candidate; +	const char *name; +	char xpath[XPATH_MAXLEN]; +	char xpath_buf[XPATH_MAXLEN - 3]; + +	if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE) +		return NB_OK; + +	/* the candidate name is fixed after setting it once, this is checked +	 * here */ +	if (args->event == NB_EV_VALIDATE) { +		/* first get the precise path to the candidate path */ +		yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf)); +		snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/.."); + +		candidate = nb_running_get_entry_non_rec(NULL, xpath, false); + +		/* then check if it exists and if the name was provided */ +		if (candidate && strlen(candidate->name) > 0) { +			flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, +				  "The candidate name is fixed!"); +			return NB_ERR_RESOURCE; +		} else +			return NB_OK; +	} + +	candidate = nb_running_get_entry(args->dnode, NULL, true); + +	name = yang_dnode_get_string(args->dnode, NULL); +	strlcpy(candidate->name, name, sizeof(candidate->name)); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + + +static int affinity_filter_modify(struct nb_cb_modify_args *args, +				  enum affinity_filter_type type) +{ +	uint32_t filter; +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	assert(args->context != NULL); +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	filter = yang_dnode_get_uint32(args->dnode, NULL); +	srte_candidate_set_affinity_filter(candidate, type, filter); + +	return NB_OK; +} + +static int affinity_filter_destroy(struct nb_cb_destroy_args *args, +				   enum affinity_filter_type type) +{ +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	assert(args->context != NULL); +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	srte_candidate_unset_affinity_filter(candidate, type); + +	return NB_OK; +} + +/* + * XPath: + * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any + */ + +int pathd_srte_policy_candidate_path_exclude_any_modify( +	struct nb_cb_modify_args *args) +{ +	return affinity_filter_modify(args, AFFINITY_FILTER_EXCLUDE_ANY); +} + +int pathd_srte_policy_candidate_path_exclude_any_destroy( +	struct nb_cb_destroy_args *args) +{ +	return affinity_filter_destroy(args, AFFINITY_FILTER_EXCLUDE_ANY); +} + + +/* + * XPath: + * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any + */ +int pathd_srte_policy_candidate_path_include_any_modify( +	struct nb_cb_modify_args *args) +{ +	return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ANY); +} + +int pathd_srte_policy_candidate_path_include_any_destroy( +	struct nb_cb_destroy_args *args) +{ +	return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ANY); +} + + +/* + * XPath: + * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all + */ +int pathd_srte_policy_candidate_path_include_all_modify( +	struct nb_cb_modify_args *args) +{ +	return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ALL); +} + +int pathd_srte_policy_candidate_path_include_all_destroy( +	struct nb_cb_destroy_args *args) +{ +	return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ALL); +} + + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics + */ +int pathd_srte_policy_candidate_path_metrics_destroy( +	struct nb_cb_destroy_args *args) +{ +	struct srte_candidate *candidate; +	enum srte_candidate_metric_type type; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	assert(args->context != NULL); +	candidate = nb_running_get_entry(args->dnode, NULL, true); + +	type = yang_dnode_get_enum(args->dnode, "./type"); +	srte_candidate_unset_metric(candidate, type); + +	return NB_OK; +} + +void pathd_srte_policy_candidate_path_metrics_apply_finish( +	struct nb_cb_apply_finish_args *args) +{ +	struct srte_candidate *candidate; +	enum srte_candidate_metric_type type; +	float value; +	bool required, is_bound = false, is_computed = false; + +	assert(args->context != NULL); + +	candidate = nb_running_get_entry(args->dnode, NULL, true); + +	type = yang_dnode_get_enum(args->dnode, "./type"); +	value = (float)yang_dnode_get_dec64(args->dnode, "./value"); +	required = yang_dnode_get_bool(args->dnode, "./required"); +	if (yang_dnode_exists(args->dnode, "./is-bound")) +		is_bound = yang_dnode_get_bool(args->dnode, "./is-bound"); +	if (yang_dnode_exists(args->dnode, "./is-computed")) +		is_computed = yang_dnode_get_bool(args->dnode, "./is-computed"); + +	srte_candidate_set_metric(candidate, type, value, required, is_bound, +				  is_computed); +} + +/* + * XPath: + * /frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function + */ +int pathd_srte_policy_candidate_path_objfun_destroy( +	struct nb_cb_destroy_args *args) +{ +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	assert(args->context != NULL); + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	srte_candidate_unset_objfun(candidate); + +	return NB_OK; +} + +void pathd_srte_policy_candidate_path_objfun_apply_finish( +	struct nb_cb_apply_finish_args *args) +{ +	struct srte_candidate *candidate; +	enum objfun_type type; +	bool required; + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	required = yang_dnode_get_bool(args->dnode, "./required"); +	type = yang_dnode_get_enum(args->dnode, "./type"); +	srte_candidate_set_objfun(candidate, required, type); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/protocol-origin + */ +int pathd_srte_policy_candidate_path_protocol_origin_modify( +	struct nb_cb_modify_args *args) +{ +	struct srte_candidate *candidate; +	enum srte_protocol_origin protocol_origin; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	protocol_origin = yang_dnode_get_enum(args->dnode, NULL); +	candidate->protocol_origin = protocol_origin; +	candidate->lsp->protocol_origin = protocol_origin; +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/originator + */ +int pathd_srte_policy_candidate_path_originator_modify( +	struct nb_cb_modify_args *args) +{ +	struct srte_candidate *candidate; +	const char *originator; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	originator = yang_dnode_get_string(args->dnode, NULL); +	strlcpy(candidate->originator, originator, +		sizeof(candidate->originator)); +	strlcpy(candidate->lsp->originator, originator, +		sizeof(candidate->lsp->originator)); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/type + */ +int pathd_srte_policy_candidate_path_type_modify(struct nb_cb_modify_args *args) +{ +	struct srte_candidate *candidate; +	enum srte_candidate_type type; +	char xpath[XPATH_MAXLEN]; +	char xpath_buf[XPATH_MAXLEN - 3]; + +	if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE) +		return NB_OK; + +	/* the candidate type is fixed after setting it once, this is checked +	 * here */ +	if (args->event == NB_EV_VALIDATE) { +		/* first get the precise path to the candidate path */ +		yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf)); +		snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/.."); + +		candidate = nb_running_get_entry_non_rec(NULL, xpath, false); + +		/* then check if it exists and if the type was provided */ +		if (candidate +		    && candidate->type != SRTE_CANDIDATE_TYPE_UNDEFINED) { +			flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, +				  "The candidate type is fixed!"); +			return NB_ERR_RESOURCE; +		} else +			return NB_OK; +	} + +	candidate = nb_running_get_entry(args->dnode, NULL, true); + +	type = yang_dnode_get_enum(args->dnode, NULL); +	candidate->type = type; +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/segment-list-name + */ +int pathd_srte_policy_candidate_path_segment_list_name_modify( +	struct nb_cb_modify_args *args) +{ +	struct srte_candidate *candidate; +	const char *segment_list_name; + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	segment_list_name = yang_dnode_get_string(args->dnode, NULL); + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	candidate->segment_list = srte_segment_list_find(segment_list_name); +	candidate->lsp->segment_list = candidate->segment_list; +	assert(candidate->segment_list); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + +int pathd_srte_policy_candidate_path_segment_list_name_destroy( +	struct nb_cb_destroy_args *args) +{ +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	candidate->segment_list = NULL; +	candidate->lsp->segment_list = NULL; +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); + +	return NB_OK; +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth + */ +void pathd_srte_policy_candidate_path_bandwidth_apply_finish( +	struct nb_cb_apply_finish_args *args) +{ +	struct srte_candidate *candidate; +	float value; +	bool required; + +	assert(args->context != NULL); + +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	value = (float)yang_dnode_get_dec64(args->dnode, "./value"); +	required = yang_dnode_get_bool(args->dnode, "./required"); +	srte_candidate_set_bandwidth(candidate, value, required); +} + +int pathd_srte_policy_candidate_path_bandwidth_destroy( +	struct nb_cb_destroy_args *args) +{ +	struct srte_candidate *candidate; + +	if (args->event != NB_EV_APPLY) +		return NB_OK; + +	assert(args->context != NULL); +	candidate = nb_running_get_entry(args->dnode, NULL, true); +	srte_candidate_unset_bandwidth(candidate); +	return NB_OK; +} diff --git a/pathd/path_nb_state.c b/pathd/path_nb_state.c new file mode 100644 index 0000000000..60f04f45c1 --- /dev/null +++ b/pathd/path_nb_state.c @@ -0,0 +1,189 @@ +/* + * 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 "prefix.h" +#include "table.h" +#include "command.h" +#include "northbound.h" +#include "libfrr.h" + +#include "pathd/pathd.h" +#include "pathd/path_nb.h" + +/* + * XPath: /frr-pathd:pathd/srte/segment-list + */ +const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args) +{ +	struct srte_segment_list *segment_list = +		(struct srte_segment_list *)args->list_entry; + +	if (args->list_entry == NULL) +		segment_list = +			RB_MIN(srte_segment_list_head, &srte_segment_lists); +	else +		segment_list = RB_NEXT(srte_segment_list_head, segment_list); + +	return segment_list; +} + +int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args) +{ +	const struct srte_segment_list *segment_list = +		(struct srte_segment_list *)args->list_entry; + +	args->keys->num = 1; +	snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%s", +		 segment_list->name); + +	return NB_OK; +} + +const void * +pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ +	return srte_segment_list_find(args->keys->key[0]); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy + */ +const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args) +{ +	struct srte_policy *policy = (struct srte_policy *)args->list_entry; + +	if (args->list_entry == NULL) +		policy = RB_MIN(srte_policy_head, &srte_policies); +	else +		policy = RB_NEXT(srte_policy_head, policy); + +	return policy; +} + +int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args) +{ +	const struct srte_policy *policy = +		(struct srte_policy *)args->list_entry; + +	args->keys->num = 2; +	snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u", +		 policy->color); +	ipaddr2str(&policy->endpoint, args->keys->key[1], +		   sizeof(args->keys->key[1])); + +	return NB_OK; +} + +const void *pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ +	uint32_t color; +	struct ipaddr endpoint; + +	color = yang_str2uint32(args->keys->key[0]); +	yang_str2ip(args->keys->key[1], &endpoint); + +	return srte_policy_find(color, &endpoint); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/is-operational + */ +struct yang_data * +pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args) +{ +	struct srte_policy *policy = (struct srte_policy *)args->list_entry; +	bool is_operational = false; + +	if (policy->status == SRTE_POLICY_STATUS_UP) +		is_operational = true; + +	return yang_data_new_bool(args->xpath, is_operational); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path + */ +const void * +pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args) +{ +	struct srte_policy *policy = +		(struct srte_policy *)args->parent_list_entry; +	struct srte_candidate *candidate = +		(struct srte_candidate *)args->list_entry; + +	if (args->list_entry == NULL) +		candidate = +			RB_MIN(srte_candidate_head, &policy->candidate_paths); +	else +		candidate = RB_NEXT(srte_candidate_head, candidate); + +	return candidate; +} + +int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args) +{ +	const struct srte_candidate *candidate = +		(struct srte_candidate *)args->list_entry; + +	args->keys->num = 1; +	snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u", +		 candidate->preference); + +	return NB_OK; +} + +const void *pathd_srte_policy_candidate_path_lookup_entry( +	struct nb_cb_lookup_entry_args *args) +{ +	struct srte_policy *policy = +		(struct srte_policy *)args->parent_list_entry; +	uint32_t preference; + +	preference = yang_str2uint32(args->keys->key[0]); + +	return srte_candidate_find(policy, preference); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate_path/is-best-candidate-path + */ +struct yang_data * +pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem( +	struct nb_cb_get_elem_args *args) +{ +	struct srte_candidate *candidate = +		(struct srte_candidate *)args->list_entry; + +	return yang_data_new_bool( +		args->xpath, CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)); +} + +/* + * XPath: /frr-pathd:pathd/srte/policy/candidate-path/discriminator + */ +struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem( +	struct nb_cb_get_elem_args *args) +{ +	struct srte_candidate *candidate = +		(struct srte_candidate *)args->list_entry; + +	return yang_data_new_uint32(args->xpath, candidate->discriminator); +} diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c new file mode 100644 index 0000000000..f0a22e4392 --- /dev/null +++ b/pathd/path_zebra.c @@ -0,0 +1,296 @@ +/* + * 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 "thread.h" +#include "log.h" +#include "lib_errors.h" +#include "if.h" +#include "prefix.h" +#include "zclient.h" +#include "network.h" +#include "stream.h" +#include "linklist.h" +#include "nexthop.h" +#include "vrf.h" +#include "typesafe.h" + +#include "pathd/pathd.h" +#include "pathd/path_zebra.h" + +static struct zclient *zclient; +static struct zclient *zclient_sync; + +/* Global Variables */ +bool g_has_router_id_v4 = false; +bool g_has_router_id_v6 = false; +struct in_addr g_router_id_v4; +struct in6_addr g_router_id_v6; +pthread_mutex_t g_router_id_v4_mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t g_router_id_v6_mtx = PTHREAD_MUTEX_INITIALIZER; + +/** + * Gives the IPv4 router ID received from Zebra. + * + * @param router_id The in_addr strucure where to store the router id + * @return true if the router ID was available, false otherwise + */ +bool get_ipv4_router_id(struct in_addr *router_id) +{ +	bool retval = false; +	assert(router_id != NULL); +	pthread_mutex_lock(&g_router_id_v4_mtx); +	if (g_has_router_id_v4) { +		memcpy(router_id, &g_router_id_v4, sizeof(*router_id)); +		retval = true; +	} +	pthread_mutex_unlock(&g_router_id_v4_mtx); +	return retval; +} + +/** + * Gives the IPv6 router ID received from Zebra. + * + * @param router_id The in6_addr strucure where to store the router id + * @return true if the router ID was available, false otherwise + */ +bool get_ipv6_router_id(struct in6_addr *router_id) +{ +	bool retval = false; +	assert(router_id != NULL); +	pthread_mutex_lock(&g_router_id_v6_mtx); +	if (g_has_router_id_v6) { +		memcpy(router_id, &g_router_id_v6, sizeof(*router_id)); +		retval = true; +	} +	pthread_mutex_unlock(&g_router_id_v6_mtx); +	return retval; +} + +static void path_zebra_connected(struct zclient *zclient) +{ +	struct srte_policy *policy; + +	zclient_send_reg_requests(zclient, VRF_DEFAULT); +	zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP6, +				      VRF_DEFAULT); + +	RB_FOREACH (policy, srte_policy_head, &srte_policies) { +		struct srte_candidate *candidate; +		struct srte_segment_list *segment_list; + +		candidate = policy->best_candidate; +		if (!candidate) +			continue; + +		segment_list = candidate->lsp->segment_list; +		if (!segment_list) +			continue; + +		path_zebra_add_sr_policy(policy, segment_list); +	} +} + +static int path_zebra_sr_policy_notify_status(ZAPI_CALLBACK_ARGS) +{ +	struct zapi_sr_policy zapi_sr_policy; +	struct srte_policy *policy; +	struct srte_candidate *best_candidate_path; + +	if (zapi_sr_policy_notify_status_decode(zclient->ibuf, &zapi_sr_policy)) +		return -1; + +	policy = srte_policy_find(zapi_sr_policy.color, +				  &zapi_sr_policy.endpoint); +	if (!policy) +		return -1; + +	best_candidate_path = policy->best_candidate; +	if (!best_candidate_path) +		return -1; + +	srte_candidate_status_update(best_candidate_path, +				     zapi_sr_policy.status); + +	return 0; +} + +/* Router-id update message from zebra. */ +static int path_zebra_router_id_update(ZAPI_CALLBACK_ARGS) +{ +	struct prefix pref; +	const char *family; +	char buf[PREFIX2STR_BUFFER]; +	zebra_router_id_update_read(zclient->ibuf, &pref); +	if (pref.family == AF_INET) { +		pthread_mutex_lock(&g_router_id_v4_mtx); +		memcpy(&g_router_id_v4, &pref.u.prefix4, +		       sizeof(g_router_id_v4)); +		g_has_router_id_v4 = true; +		inet_ntop(AF_INET, &g_router_id_v4, buf, sizeof(buf)); +		pthread_mutex_unlock(&g_router_id_v4_mtx); +		family = "IPv4"; +	} else if (pref.family == AF_INET6) { +		pthread_mutex_lock(&g_router_id_v6_mtx); +		memcpy(&g_router_id_v6, &pref.u.prefix6, +		       sizeof(g_router_id_v6)); +		g_has_router_id_v6 = true; +		inet_ntop(AF_INET6, &g_router_id_v6, buf, sizeof(buf)); +		pthread_mutex_unlock(&g_router_id_v6_mtx); +		family = "IPv6"; +	} else { +		pthread_mutex_unlock(&g_router_id_v4_mtx); +		zlog_warn("Unexpected router ID address family for vrf %u: %u", +			  vrf_id, pref.family); +		return 0; +	} +	pthread_mutex_unlock(&g_router_id_v4_mtx); +	zlog_info("%s Router Id updated for VRF %u: %s", family, vrf_id, buf); +	return 0; +} + +/** + * Adds a segment routing policy to Zebra. + * + * @param policy The policy to add + * @param segment_list The segment list for the policy + */ +void path_zebra_add_sr_policy(struct srte_policy *policy, +			      struct srte_segment_list *segment_list) +{ +	struct zapi_sr_policy zp = {}; +	struct srte_segment_entry *segment; + +	zp.color = policy->color; +	zp.endpoint = policy->endpoint; +	strlcpy(zp.name, policy->name, sizeof(zp.name)); +	zp.segment_list.type = ZEBRA_LSP_SRTE; +	zp.segment_list.local_label = policy->binding_sid; +	zp.segment_list.label_num = 0; +	RB_FOREACH (segment, srte_segment_entry_head, &segment_list->segments) +		zp.segment_list.labels[zp.segment_list.label_num++] = +			segment->sid_value; +	policy->status = SRTE_POLICY_STATUS_GOING_UP; + +	(void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_SET, &zp); +} + +/** + * Deletes a segment policy from Zebra. + * + * @param policy The policy to remove + */ +void path_zebra_delete_sr_policy(struct srte_policy *policy) +{ +	struct zapi_sr_policy zp = {}; + +	zp.color = policy->color; +	zp.endpoint = policy->endpoint; +	strlcpy(zp.name, policy->name, sizeof(zp.name)); +	zp.segment_list.type = ZEBRA_LSP_SRTE; +	zp.segment_list.local_label = policy->binding_sid; +	zp.segment_list.label_num = 0; +	policy->status = SRTE_POLICY_STATUS_DOWN; + +	(void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_DELETE, &zp); +} + +/** + * Allocates a label from Zebra's label manager. + * + * @param label the label to be allocated + * @return 0 if the label has been allocated, -1 otherwise + */ +int path_zebra_request_label(mpls_label_t label) +{ +	int ret; +	uint32_t start, end; + +	ret = lm_get_label_chunk(zclient_sync, 0, label, 1, &start, &end); +	if (ret < 0) { +		zlog_warn("%s: error getting label range!", __func__); +		return -1; +	} + +	return 0; +} + +/** + * Releases a previously allocated label from Zebra's label manager. + * + * @param label The label to release + * @return 0 ifthe label has beel released, -1 otherwise + */ +void path_zebra_release_label(mpls_label_t label) +{ +	int ret; + +	ret = lm_release_label_chunk(zclient_sync, label, label); +	if (ret < 0) +		zlog_warn("%s: error releasing label range!", __func__); +} + +static void path_zebra_label_manager_connect(void) +{ +	/* Connect to label manager. */ +	while (zclient_socket_connect(zclient_sync) < 0) { +		zlog_warn("%s: error connecting synchronous zclient!", +			  __func__); +		sleep(1); +	} +	set_nonblocking(zclient_sync->sock); + +	/* Send hello to notify zebra this is a synchronous client */ +	while (zclient_send_hello(zclient_sync) < 0) { +		zlog_warn("%s: Error sending hello for synchronous zclient!", +			  __func__); +		sleep(1); +	} + +	while (lm_label_manager_connect(zclient_sync, 0) != 0) { +		zlog_warn("%s: error connecting to label manager!", __func__); +		sleep(1); +	} +} + +/** + * Initializes Zebra asynchronous connection. + * + * @param master The master thread + */ +void path_zebra_init(struct thread_master *master) +{ +	struct zclient_options options = zclient_options_default; +	options.synchronous = true; + +	/* Initialize asynchronous zclient. */ +	zclient = zclient_new(master, &zclient_options_default); +	zclient_init(zclient, ZEBRA_ROUTE_SRTE, 0, &pathd_privs); +	zclient->zebra_connected = path_zebra_connected; +	zclient->sr_policy_notify_status = path_zebra_sr_policy_notify_status; +	zclient->router_id_update = path_zebra_router_id_update; + +	/* Initialize special zclient for synchronous message exchanges. */ +	zclient_sync = zclient_new(master, &options); +	zclient_sync->sock = -1; +	zclient_sync->redist_default = ZEBRA_ROUTE_SRTE; +	zclient_sync->instance = 1; +	zclient_sync->privs = &pathd_privs; + +	/* Connect to the LM. */ +	path_zebra_label_manager_connect(); +} diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h new file mode 100644 index 0000000000..42a7123dd4 --- /dev/null +++ b/pathd/path_zebra.h @@ -0,0 +1,34 @@ +/* + * 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_MPLS_H_ +#define _FRR_PATH_MPLS_H_ + +#include <zebra.h> +#include "pathd/pathd.h" + +bool get_ipv4_router_id(struct in_addr *router_id); +bool get_ipv6_router_id(struct in6_addr *router_id); +void path_zebra_add_sr_policy(struct srte_policy *policy, +			      struct srte_segment_list *segment_list); +void path_zebra_delete_sr_policy(struct srte_policy *policy); +int path_zebra_request_label(mpls_label_t label); +void path_zebra_release_label(mpls_label_t label); +void path_zebra_init(struct thread_master *master); + +#endif /* _FRR_PATH_MPLS_H_ */ diff --git a/pathd/pathd.c b/pathd/pathd.c new file mode 100644 index 0000000000..2e2fa86714 --- /dev/null +++ b/pathd/pathd.c @@ -0,0 +1,1127 @@ +/* + * 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 "log.h" +#include "lib_errors.h" + +#include "pathd/pathd.h" +#include "pathd/path_memory.h" +#include "pathd/path_zebra.h" +#include "pathd/path_debug.h" + +#define HOOK_DELAY 3 + +DEFINE_MTYPE_STATIC(PATHD, PATH_SEGMENT_LIST, "Segment List") +DEFINE_MTYPE_STATIC(PATHD, PATH_SR_POLICY, "SR Policy") +DEFINE_MTYPE_STATIC(PATHD, PATH_SR_CANDIDATE, "SR Policy candidate path") + +DEFINE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate), +	    (candidate)) +DEFINE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate), +	    (candidate)) +DEFINE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate), +	    (candidate)) + +static void trigger_pathd_candidate_created(struct srte_candidate *candidate); +static int trigger_pathd_candidate_created_timer(struct thread *thread); +static void trigger_pathd_candidate_updated(struct srte_candidate *candidate); +static int trigger_pathd_candidate_updated_timer(struct thread *thread); +static void trigger_pathd_candidate_removed(struct srte_candidate *candidate); +static const char * +srte_candidate_metric_name(enum srte_candidate_metric_type type); + +static void srte_set_metric(struct srte_metric *metric, float value, +			    bool required, bool is_bound, bool is_computed); +static void srte_unset_metric(struct srte_metric *metric); + + +/* Generate rb-tree of Segment List Segment instances. */ +static inline int srte_segment_entry_compare(const struct srte_segment_entry *a, +					     const struct srte_segment_entry *b) +{ +	return a->index - b->index; +} +RB_GENERATE(srte_segment_entry_head, srte_segment_entry, entry, +	    srte_segment_entry_compare) + +/* Generate rb-tree of Segment List instances. */ +static inline int srte_segment_list_compare(const struct srte_segment_list *a, +					    const struct srte_segment_list *b) +{ +	return strcmp(a->name, b->name); +} +RB_GENERATE(srte_segment_list_head, srte_segment_list, entry, +	    srte_segment_list_compare) + +struct srte_segment_list_head srte_segment_lists = +	RB_INITIALIZER(&srte_segment_lists); + +/* Generate rb-tree of Candidate Path instances. */ +static inline int srte_candidate_compare(const struct srte_candidate *a, +					 const struct srte_candidate *b) +{ +	return a->preference - b->preference; +} +RB_GENERATE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare) + +/* Generate rb-tree of SR Policy instances. */ +static inline int srte_policy_compare(const struct srte_policy *a, +				      const struct srte_policy *b) +{ +	return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, +				 b->color); +} +RB_GENERATE(srte_policy_head, srte_policy, entry, srte_policy_compare) + +struct srte_policy_head srte_policies = RB_INITIALIZER(&srte_policies); + +/** + * Adds a segment list to pathd. + * + * @param name The name of the segment list to add + * @return The added segment list + */ +struct srte_segment_list *srte_segment_list_add(const char *name) +{ +	struct srte_segment_list *segment_list; + +	segment_list = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment_list)); +	strlcpy(segment_list->name, name, sizeof(segment_list->name)); +	RB_INIT(srte_segment_entry_head, &segment_list->segments); +	RB_INSERT(srte_segment_list_head, &srte_segment_lists, segment_list); + +	return segment_list; +} + +/** + * Deletes a segment list from pathd. + * + * The given segment list structure will be freed and should not be used anymore + * after calling this function. + * + * @param segment_list the segment list to remove from pathd. + */ +void srte_segment_list_del(struct srte_segment_list *segment_list) +{ +	struct srte_segment_entry *segment, *safe_seg; +	RB_FOREACH_SAFE (segment, srte_segment_entry_head, +			 &segment_list->segments, safe_seg) { +		srte_segment_entry_del(segment); +	} +	RB_REMOVE(srte_segment_list_head, &srte_segment_lists, segment_list); +	XFREE(MTYPE_PATH_SEGMENT_LIST, segment_list); +} + +/** + * Search for a segment list by name. + * + * @param name The name of the segment list to look for + * @return The segment list if found, NULL otherwise + */ +struct srte_segment_list *srte_segment_list_find(const char *name) +{ +	struct srte_segment_list search; + +	strlcpy(search.name, name, sizeof(search.name)); +	return RB_FIND(srte_segment_list_head, &srte_segment_lists, &search); +} + +/** + * Adds a segment to a segment list. + * + * @param segment_list The segment list the segment should be added to + * @param index	The index of the added segment in the segment list + * @return The added segment + */ +struct srte_segment_entry * +srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index) +{ +	struct srte_segment_entry *segment; + +	segment = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment)); +	segment->segment_list = segment_list; +	segment->index = index; +	RB_INSERT(srte_segment_entry_head, &segment_list->segments, segment); + +	return segment; +} + +/** + * Deletes a segment from a segment list. + * + * @param segment The segment to be removed + */ +void srte_segment_entry_del(struct srte_segment_entry *segment) +{ +	RB_REMOVE(srte_segment_entry_head, &segment->segment_list->segments, +		  segment); +	XFREE(MTYPE_PATH_SEGMENT_LIST, segment); +} + +/** + * Set the node or adjacency identifier of a segment. + * + * @param segment The segment for which the NAI should be set + * @param type The type of the NAI + * @param type The address of the node or the local address of the adjacency + * @param type The local interface index of the unumbered adjacency + * @param type The remote address of the adjacency + * @param type The remote interface index of the unumbered adjacency + */ +void srte_segment_entry_set_nai(struct srte_segment_entry *segment, +				enum srte_segment_nai_type type, +				struct ipaddr *local_ip, uint32_t local_iface, +				struct ipaddr *remote_ip, uint32_t remote_iface) +{ +	segment->nai_type = type; +	memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr)); + +	switch (type) { +	case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE: +	case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE: +		break; +	case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY: +	case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY: +		memcpy(&segment->nai_remote_addr, remote_ip, +		       sizeof(struct ipaddr)); +		break; +	case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY: +		memcpy(&segment->nai_remote_addr, remote_ip, +		       sizeof(struct ipaddr)); +		segment->nai_local_iface = local_iface; +		segment->nai_remote_iface = remote_iface; +		break; +	default: +		segment->nai_local_addr.ipa_type = IPADDR_NONE; +		segment->nai_local_iface = 0; +		segment->nai_remote_addr.ipa_type = IPADDR_NONE; +		segment->nai_remote_iface = 0; +	} +} + +/** + * Add a policy to pathd. + * + * WARNING: The color 0 is a special case as it is the no-color. + * + * @param color The color of the policy. + * @param endpoint The IP address of the policy endpoint + * @return The created policy + */ +struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint) +{ +	struct srte_policy *policy; + +	policy = XCALLOC(MTYPE_PATH_SR_POLICY, sizeof(*policy)); +	policy->color = color; +	policy->endpoint = *endpoint; +	policy->binding_sid = MPLS_LABEL_NONE; +	RB_INIT(srte_candidate_head, &policy->candidate_paths); +	RB_INSERT(srte_policy_head, &srte_policies, policy); + +	return policy; +} + +/** + * Delete a policy from pathd. + * + * The given policy structure will be freed and should never be used again + * after calling this function. + * + * @param policy The policy to be removed + */ +void srte_policy_del(struct srte_policy *policy) +{ +	struct srte_candidate *candidate; + +	path_zebra_delete_sr_policy(policy); + +	path_zebra_release_label(policy->binding_sid); + +	while (!RB_EMPTY(srte_candidate_head, &policy->candidate_paths)) { +		candidate = +			RB_ROOT(srte_candidate_head, &policy->candidate_paths); +		trigger_pathd_candidate_removed(candidate); +		srte_candidate_del(candidate); +	} + +	RB_REMOVE(srte_policy_head, &srte_policies, policy); +	XFREE(MTYPE_PATH_SR_POLICY, policy); +} + +/** + * Search for a policy by color and endpoint. + * + * WARNING: The color 0 is a special case as it is the no-color. + * + * @param color The color of the policy to look for + * @param endpoint The endpoint of the policy to look for + * @return The policy if found, NULL otherwise + */ +struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint) +{ +	struct srte_policy search; + +	search.color = color; +	search.endpoint = *endpoint; +	return RB_FIND(srte_policy_head, &srte_policies, &search); +} + +/** + * Update a policy binding SID. + * + * @param policy The policy for which the SID should be updated + * @param binding_sid The new binding SID for the given policy + */ +void srte_policy_update_binding_sid(struct srte_policy *policy, +				    uint32_t binding_sid) +{ +	if (policy->binding_sid != MPLS_LABEL_NONE) +		path_zebra_release_label(policy->binding_sid); + +	policy->binding_sid = binding_sid; + +	/* Reinstall the Binding-SID if necessary. */ +	if (policy->best_candidate) +		path_zebra_add_sr_policy( +			policy, policy->best_candidate->lsp->segment_list); +} + +/** + * Gives the policy best candidate path. + * + * @param policy The policy we want the best candidate path from + * @return The best candidate path + */ +static struct srte_candidate * +srte_policy_best_candidate(const struct srte_policy *policy) +{ +	struct srte_candidate *candidate; + +	RB_FOREACH_REVERSE (candidate, srte_candidate_head, +			    &policy->candidate_paths) { +		/* search for highest preference with existing segment list */ +		if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED) +		    && candidate->lsp->segment_list) +			return candidate; +	} + +	return NULL; +} + +/** + * Apply changes defined by setting the policies, candidate paths + * and segment lists modification flags NEW, MODIFIED and DELETED. + * + * This allows the northbound code to delay all the side effects of adding + * modifying and deleting them to the end. + * + * Example of marking an object as modified: + *   `SET_FLAG(obj->flags, F_XXX_MODIFIED)` + */ +void srte_apply_changes(void) +{ +	struct srte_policy *policy, *safe_pol; +	struct srte_segment_list *segment_list, *safe_sl; + +	RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) { +		if (CHECK_FLAG(policy->flags, F_POLICY_DELETED)) { +			srte_policy_del(policy); +			continue; +		} +		srte_policy_apply_changes(policy); +		UNSET_FLAG(policy->flags, F_POLICY_NEW); +		UNSET_FLAG(policy->flags, F_POLICY_MODIFIED); +	} + +	RB_FOREACH_SAFE (segment_list, srte_segment_list_head, +			 &srte_segment_lists, safe_sl) { +		if (CHECK_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED)) { +			srte_segment_list_del(segment_list); +			continue; +		} +		UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW); +		UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED); +	} +} + +/** + * Apply changes defined by setting the given policy and its candidate paths + * modification flags NEW, MODIFIED and DELETED. + * + * In moste cases `void srte_apply_changes(void)` should be used instead, + * this function will not handle the changes of segment lists used by the + * policy. + * + * @param policy The policy changes has to be applied to. + */ +void srte_policy_apply_changes(struct srte_policy *policy) +{ +	struct srte_candidate *candidate, *safe; +	struct srte_candidate *old_best_candidate; +	struct srte_candidate *new_best_candidate; +	char endpoint[46]; + +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + +	/* Get old and new best candidate path. */ +	old_best_candidate = policy->best_candidate; +	new_best_candidate = srte_policy_best_candidate(policy); + +	if (new_best_candidate != old_best_candidate) { +		/* TODO: add debug guard. */ +		zlog_debug( +			"SR-TE(%s, %u): best candidate changed from %s to %s", +			endpoint, policy->color, +			old_best_candidate ? old_best_candidate->name : "none", +			new_best_candidate ? new_best_candidate->name : "none"); + +		if (old_best_candidate) { +			policy->best_candidate = NULL; +			UNSET_FLAG(old_best_candidate->flags, F_CANDIDATE_BEST); +			SET_FLAG(old_best_candidate->flags, +				 F_CANDIDATE_MODIFIED); + +			/* +			 * Rely on replace semantics if there's a new best +			 * candidate. +			 */ +			if (!new_best_candidate) +				path_zebra_delete_sr_policy(policy); +		} +		if (new_best_candidate) { +			policy->best_candidate = new_best_candidate; +			SET_FLAG(new_best_candidate->flags, F_CANDIDATE_BEST); +			SET_FLAG(new_best_candidate->flags, +				 F_CANDIDATE_MODIFIED); + +			path_zebra_add_sr_policy( +				policy, new_best_candidate->lsp->segment_list); +		} +	} else if (new_best_candidate) { +		/* The best candidate path did not change, but some of its +		 * attributes or its segment list may have changed. +		 */ + +		bool candidate_changed = CHECK_FLAG(new_best_candidate->flags, +						    F_CANDIDATE_MODIFIED); +		bool segment_list_changed = +			new_best_candidate->lsp->segment_list +			&& CHECK_FLAG( +				   new_best_candidate->lsp->segment_list->flags, +				   F_SEGMENT_LIST_MODIFIED); + +		if (candidate_changed || segment_list_changed) { +			/* TODO: add debug guard. */ +			zlog_debug("SR-TE(%s, %u): best candidate %s changed", +				   endpoint, policy->color, +				   new_best_candidate->name); + +			path_zebra_add_sr_policy( +				policy, new_best_candidate->lsp->segment_list); +		} +	} + +	RB_FOREACH_SAFE (candidate, srte_candidate_head, +			 &policy->candidate_paths, safe) { +		if (CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)) { +			trigger_pathd_candidate_removed(candidate); +			srte_candidate_del(candidate); +			continue; +		} else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_NEW)) { +			trigger_pathd_candidate_created(candidate); +		} else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_MODIFIED)) { +			trigger_pathd_candidate_updated(candidate); +		} else if (candidate->lsp->segment_list +			   && CHECK_FLAG(candidate->lsp->segment_list->flags, +					 F_SEGMENT_LIST_MODIFIED)) { +			trigger_pathd_candidate_updated(candidate); +		} + +		UNSET_FLAG(candidate->flags, F_CANDIDATE_NEW); +		UNSET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	} +} + +/** + * Adds a candidate path to a policy. + * + * @param policy The policy the candidate path should be added to + * @param preference The preference of the candidate path to be added + * @return The added candidate path + */ +struct srte_candidate *srte_candidate_add(struct srte_policy *policy, +					  uint32_t preference) +{ +	struct srte_candidate *candidate; +	struct srte_lsp *lsp; + +	candidate = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*candidate)); +	lsp = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*lsp)); + +	candidate->preference = preference; +	candidate->policy = policy; +	candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED; +	candidate->discriminator = rand(); + +	lsp->candidate = candidate; +	candidate->lsp = lsp; + +	RB_INSERT(srte_candidate_head, &policy->candidate_paths, candidate); + +	return candidate; +} + +/** + * Deletes a candidate. + * + * The corresponding LSP will be removed alongside the candidate path. + * The given candidate will be freed and shouldn't be used anymore after the + * calling this function. + * + * @param candidate The candidate path to delete + */ +void srte_candidate_del(struct srte_candidate *candidate) +{ +	struct srte_policy *srte_policy = candidate->policy; + +	RB_REMOVE(srte_candidate_head, &srte_policy->candidate_paths, +		  candidate); + +	XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp); +	XFREE(MTYPE_PATH_SR_CANDIDATE, candidate); +} + +/** + * Sets the bandwidth constraint of given candidate path. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the bandwidth should be changed + * @param bandwidth The Bandwidth constraint to set to the candidate path + * @param required If the constraint is required (true) or only desired (false) + */ +void srte_candidate_set_bandwidth(struct srte_candidate *candidate, +				  float bandwidth, bool required) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug( +		"SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s", +		endpoint, policy->color, candidate->name, +		required ? "required " : "", bandwidth); +	SET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH); +	COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required); +	candidate->bandwidth = bandwidth; + +	srte_lsp_set_bandwidth(candidate->lsp, bandwidth, required); +} + +/** + * Sets the bandwidth constraint of the given LSP. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The lsp of which the bandwidth should be changed + * @param bandwidth The Bandwidth constraint to set to the candidate path + * @param required If the constraint is required (true) or only desired (false) + */ +void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth, +			    bool required) +{ +	struct srte_candidate *candidate = lsp->candidate; +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s", +		   endpoint, policy->color, candidate->name, +		   required ? "required" : "", bandwidth); +	SET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); +	COND_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required); +	lsp->bandwidth = bandwidth; +} + +/** + * Remove a candidate path bandwidth constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the bandwidth should be removed + */ +void srte_candidate_unset_bandwidth(struct srte_candidate *candidate) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): candidate %s config bandwidth unset", +		   endpoint, policy->color, candidate->name); +	UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH); +	UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); +	candidate->bandwidth = 0; +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	srte_lsp_unset_bandwidth(candidate->lsp); +} + +/** + * Remove an LSP bandwidth constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The lsp of which the bandwidth should be changed + */ +void srte_lsp_unset_bandwidth(struct srte_lsp *lsp) +{ +	struct srte_candidate *candidate = lsp->candidate; +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): candidate %s lsp bandwidth unset", endpoint, +		   policy->color, candidate->name); +	UNSET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH); +	UNSET_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	lsp->bandwidth = 0; +} + +/** + * Sets a candidate path metric constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path of which the metric should be changed + * @param type The metric type + * @param value The metric value + * @param required If the constraint is required (true) or only desired (false) + * @param is_bound If the metric is an indicative value or a strict upper bound + * @param is_computed If the metric was computed or configured + */ +void srte_candidate_set_metric(struct srte_candidate *candidate, +			       enum srte_candidate_metric_type type, +			       float value, bool required, bool is_bound, +			       bool is_computed) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug( +		"SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f " +		"(is-bound: %s; is_computed: %s)", +		endpoint, policy->color, candidate->name, +		required ? "required " : "", srte_candidate_metric_name(type), +		type, value, is_bound ? "true" : "false", +		is_computed ? "true" : "false"); +	assert((type > 0) && (type <= MAX_METRIC_TYPE)); +	srte_set_metric(&candidate->metrics[type - 1], value, required, +			is_bound, is_computed); +	srte_lsp_set_metric(candidate->lsp, type, value, required, is_bound, +			    is_computed); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +} + +/** + * Sets an LSP metric constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The LSP of which the metric should be changed + * @param type The metric type + * @param value The metric value + * @param required If the constraint is required (true) or only desired (false) + * @param is_bound If the metric is an indicative value or a strict upper bound + * @param is_computed If the metric was computed or configured + */ +void srte_lsp_set_metric(struct srte_lsp *lsp, +			 enum srte_candidate_metric_type type, float value, +			 bool required, bool is_bound, bool is_computed) +{ +	struct srte_candidate *candidate = lsp->candidate; +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug( +		"SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f " +		"(is-bound: %s; is_computed: %s)", +		endpoint, policy->color, candidate->name, +		required ? "required " : "", srte_candidate_metric_name(type), +		type, value, is_bound ? "true" : "false", +		is_computed ? "true" : "false"); +	assert((type > 0) && (type <= MAX_METRIC_TYPE)); +	srte_set_metric(&lsp->metrics[type - 1], value, required, is_bound, +			is_computed); +} + +void srte_set_metric(struct srte_metric *metric, float value, bool required, +		     bool is_bound, bool is_computed) +{ +	SET_FLAG(metric->flags, F_METRIC_IS_DEFINED); +	COND_FLAG(metric->flags, F_METRIC_IS_REQUIRED, required); +	COND_FLAG(metric->flags, F_METRIC_IS_BOUND, is_bound); +	COND_FLAG(metric->flags, F_METRIC_IS_COMPUTED, is_computed); +	metric->value = value; +} + +/** + * Removes a candidate path metric constraint. + * + * The corresponding LSP will be changed too. + * + * @param candidate The candidate path from which the metric should be removed + * @param type The metric type + */ +void srte_candidate_unset_metric(struct srte_candidate *candidate, +				 enum srte_candidate_metric_type type) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): candidate %s config metric %s (%u) unset", +		   endpoint, policy->color, candidate->name, +		   srte_candidate_metric_name(type), type); +	assert((type > 0) && (type <= MAX_METRIC_TYPE)); +	srte_unset_metric(&candidate->metrics[type - 1]); +	srte_lsp_unset_metric(candidate->lsp, type); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +} + +/** + * Removes a candidate path metric constraint. + * + * The changes will not be shown as part of the running configuration. + * + * @param lsp The LSP from which the metric should be removed + * @param type The metric type + */ +void srte_lsp_unset_metric(struct srte_lsp *lsp, +			   enum srte_candidate_metric_type type) +{ +	struct srte_candidate *candidate = lsp->candidate; +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset", +		   endpoint, policy->color, candidate->name, +		   srte_candidate_metric_name(type), type); +	assert((type > 0) && (type <= MAX_METRIC_TYPE)); +	srte_unset_metric(&lsp->metrics[type - 1]); +} + +void srte_unset_metric(struct srte_metric *metric) +{ +	UNSET_FLAG(metric->flags, F_METRIC_IS_DEFINED); +	UNSET_FLAG(metric->flags, F_METRIC_IS_BOUND); +	UNSET_FLAG(metric->flags, F_METRIC_IS_COMPUTED); +	metric->value = 0; +} + +/** + * Sets a candidate path objective function. + * + * @param candidate The candidate path of which the OF should be changed + * @param required If the constraint is required (true) or only desired (false) + * @param type The objective function type + */ +void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required, +			       enum objfun_type type) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + +	candidate->objfun = type; +	SET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); +	COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN, required); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	zlog_debug("SR-TE(%s, %u): candidate %s %sobjective function set to %s", +		   endpoint, policy->color, candidate->name, +		   required ? "required " : "", objfun_type_name(type)); +} + +/** + * Removed the objective function constraint from a candidate path. + * + * @param candidate The candidate path from which the OF should be removed + */ +void srte_candidate_unset_objfun(struct srte_candidate *candidate) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + +	UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN); +	UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	candidate->objfun = OBJFUN_UNDEFINED; +	zlog_debug( +		"SR-TE(%s, %u): candidate %s objective functions preferences unset", +		endpoint, policy->color, candidate->name); +} + +static uint32_t filter_type_to_flag(enum affinity_filter_type type) +{ +	switch (type) { +	case AFFINITY_FILTER_EXCLUDE_ANY: +		return F_CANDIDATE_HAS_EXCLUDE_ANY; +	case AFFINITY_FILTER_INCLUDE_ANY: +		return F_CANDIDATE_HAS_INCLUDE_ANY; +	case AFFINITY_FILTER_INCLUDE_ALL: +		return F_CANDIDATE_HAS_INCLUDE_ALL; +	default: +		return 0; +	} +} + +static const char *filter_type_name(enum affinity_filter_type type) +{ +	switch (type) { +	case AFFINITY_FILTER_EXCLUDE_ANY: +		return "exclude-any"; +	case AFFINITY_FILTER_INCLUDE_ANY: +		return "include-any"; +	case AFFINITY_FILTER_INCLUDE_ALL: +		return "include-all"; +	default: +		return "unknown"; +	} +} + +/** + * Sets a candidate path affinity filter constraint. + * + * @param candidate The candidate path of which the constraint should be changed + * @param type The affinity constraint type to set + * @param filter The bitmask filter of the constraint + */ +void srte_candidate_set_affinity_filter(struct srte_candidate *candidate, +					enum affinity_filter_type type, +					uint32_t filter) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + +	assert(type > AFFINITY_FILTER_UNDEFINED); +	assert(type <= MAX_AFFINITY_FILTER_TYPE); +	SET_FLAG(candidate->flags, filter_type_to_flag(type)); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	candidate->affinity_filters[type - 1] = filter; +	zlog_debug( +		"SR-TE(%s, %u): candidate %s affinity filter %s set to 0x%08x", +		endpoint, policy->color, candidate->name, +		filter_type_name(type), filter); +} + +/** + * Removes a candidate path affinity filter constraint. + * + * @param candidate The candidate path from which the constraint should be + * removed + * @param type The affinity constraint type to remove + */ +void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate, +					  enum affinity_filter_type type) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); + +	assert(type > AFFINITY_FILTER_UNDEFINED); +	assert(type <= MAX_AFFINITY_FILTER_TYPE); +	UNSET_FLAG(candidate->flags, filter_type_to_flag(type)); +	SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED); +	candidate->affinity_filters[type - 1] = 0; +	zlog_debug("SR-TE(%s, %u): candidate %s affinity filter %s unset", +		   endpoint, policy->color, candidate->name, +		   filter_type_name(type)); +} + +/** + * Searches for a candidate path of the given policy. + * + * @param policy The policy to search for candidate path + * @param preference The preference of the candidate path you are looking for + * @return The candidate path if found, NULL otherwise + */ +struct srte_candidate *srte_candidate_find(struct srte_policy *policy, +					   uint32_t preference) +{ +	struct srte_candidate search; + +	search.preference = preference; +	return RB_FIND(srte_candidate_head, &policy->candidate_paths, &search); +} + +/** + * Searches for a an entry of a given segment list. + * + * @param segment_list The segment list to search for the entry + * @param index The index of the entry you are looking for + * @return The segment list entry if found, NULL otherwise. + */ +struct srte_segment_entry * +srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index) +{ +	struct srte_segment_entry search; + +	search.index = index; +	return RB_FIND(srte_segment_entry_head, &segment_list->segments, +		       &search); +} + +/** + * Updates a candidate status. + * + * @param candidate The candidate of which the status should be updated + * @param status The new candidate path status + */ +void srte_candidate_status_update(struct srte_candidate *candidate, int status) +{ +	struct srte_policy *policy = candidate->policy; +	char endpoint[46]; +	ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); +	zlog_debug("SR-TE(%s, %u): zebra updated status to %d", endpoint, +		   policy->color, status); +	switch (status) { +	case ZEBRA_SR_POLICY_DOWN: +		switch (policy->status) { +		/* If the policy is GOING_UP, and zebra faild +		   to install it, we wait for zebra to retry */ +		/* TODO: Add some timeout after which we would +				 get is back to DOWN and remove the +				 policy */ +		case SRTE_POLICY_STATUS_GOING_UP: +		case SRTE_POLICY_STATUS_DOWN: +			return; +		default: +			zlog_debug("SR-TE(%s, %u): policy is DOWN", endpoint, +				   policy->color); +			policy->status = SRTE_POLICY_STATUS_DOWN; +			break; +		} +		break; +	case ZEBRA_SR_POLICY_UP: +		switch (policy->status) { +		case SRTE_POLICY_STATUS_UP: +			return; +		default: +			zlog_debug("SR-TE(%s, %u): policy is UP", endpoint, +				   policy->color); +			policy->status = SRTE_POLICY_STATUS_UP; +			break; +		} +		break; +	} + +	trigger_pathd_candidate_updated(candidate); +} + +/** + * Flags the segment lists from give originator for removal. + * + * The function srte_apply_changes must be called afterward for + * the segment list to be removed. + * + * @param originator The originator tag of the segment list to be marked + * @param force If the unset should be forced regardless of the originator + */ +void srte_candidate_unset_segment_list(const char *originator, bool force) +{ +	if (originator == NULL) { +		zlog_warn( +			"Cannot unset segment list because originator is NULL"); +		return; +	} + +	zlog_debug("Unset segment lists for originator %s", originator); + +	/* Iterate the policies, then iterate each policy's candidate path +	 * to check the candidate path's segment list originator */ +	struct srte_policy *policy; +	RB_FOREACH (policy, srte_policy_head, &srte_policies) { +		zlog_debug("Unset segment lists checking policy %s", +			   policy->name); +		struct srte_candidate *candidate; +		RB_FOREACH (candidate, srte_candidate_head, +			    &policy->candidate_paths) { +			zlog_debug("Unset segment lists checking candidate %s", +				   candidate->name); +			if (candidate->lsp == NULL) { +				continue; +			} + +			/* The candidate->lsp->segment_list is operational data, +			 * configured by the PCE. We dont want to modify the +			 * candidate->segment_list, +			 * which is configuration data. */ +			struct srte_segment_list *segment_list = +				candidate->lsp->segment_list; +			if (segment_list == NULL) { +				continue; +			} + +			if (segment_list->protocol_origin +			    == SRTE_ORIGIN_LOCAL) { +				zlog_warn( +					"Cannot unset segment list %s because it " +					"was created locally", +					segment_list->name); +				continue; +			} + +			/* In the case of last pce,we force the unset +			 * because we don't have pce by prefix (TODO) is all +			 * 'global' */ +			if (strncmp(segment_list->originator, originator, +				    sizeof(segment_list->originator)) +				    == 0 +			    || force) { +				zlog_debug("Unset segment list %s", +					   segment_list->name); +				SET_FLAG(segment_list->flags, +					 F_SEGMENT_LIST_DELETED); +				SET_FLAG(candidate->flags, +					 F_CANDIDATE_MODIFIED); +				candidate->lsp->segment_list = NULL; +			} +		} +	} +} + +/** + * Gives a string representation of given protocol origin enum. + * + * @param origin The enum you want a string representation of + * @return The string representation of given enum + */ +const char *srte_origin2str(enum srte_protocol_origin origin) +{ +	switch (origin) { +	case SRTE_ORIGIN_PCEP: +		return "PCEP"; +	case SRTE_ORIGIN_BGP: +		return "BGP"; +	case SRTE_ORIGIN_LOCAL: +		return "Local"; +	default: +		return "Unknown"; +	} +} + +void trigger_pathd_candidate_created(struct srte_candidate *candidate) +{ +	/* The hook is called asynchronously to let the PCEP module +	time to send a response to the PCE before receiving any updates from +	pathd. In addition, a minimum amount of time need to pass before +	the hook is called to prevent the hook to be called multiple times +	from changing the candidate by hand with the console */ +	if (candidate->hook_timer != NULL) +		return; +	thread_add_timer(master, trigger_pathd_candidate_created_timer, +			 (void *)candidate, HOOK_DELAY, &candidate->hook_timer); +} + +int trigger_pathd_candidate_created_timer(struct thread *thread) +{ +	struct srte_candidate *candidate = THREAD_ARG(thread); +	candidate->hook_timer = NULL; +	return hook_call(pathd_candidate_created, candidate); +} + +void trigger_pathd_candidate_updated(struct srte_candidate *candidate) +{ +	/* The hook is called asynchronously to let the PCEP module +	time to send a response to the PCE before receiving any updates from +	pathd. In addition, a minimum amount of time need to pass before +	the hook is called to prevent the hook to be called multiple times +	from changing the candidate by hand with the console */ +	if (candidate->hook_timer != NULL) +		return; +	thread_add_timer(master, trigger_pathd_candidate_updated_timer, +			 (void *)candidate, HOOK_DELAY, &candidate->hook_timer); +} + +int trigger_pathd_candidate_updated_timer(struct thread *thread) +{ +	struct srte_candidate *candidate = THREAD_ARG(thread); +	candidate->hook_timer = NULL; +	return hook_call(pathd_candidate_updated, candidate); +} + +void trigger_pathd_candidate_removed(struct srte_candidate *candidate) +{ +	/* The hook needs to be call synchronously, otherwise the candidate +	path will be already deleted when the handler is called */ +	if (candidate->hook_timer != NULL) { +		thread_cancel(&candidate->hook_timer); +		candidate->hook_timer = NULL; +	} +	hook_call(pathd_candidate_removed, candidate); +} + +const char *srte_candidate_metric_name(enum srte_candidate_metric_type type) +{ +	switch (type) { +	case SRTE_CANDIDATE_METRIC_TYPE_IGP: +		return "IGP"; +	case SRTE_CANDIDATE_METRIC_TYPE_TE: +		return "TE"; +	case SRTE_CANDIDATE_METRIC_TYPE_HC: +		return "HC"; +	case SRTE_CANDIDATE_METRIC_TYPE_ABC: +		return "ABC"; +	case SRTE_CANDIDATE_METRIC_TYPE_LMLL: +		return "LMLL"; +	case SRTE_CANDIDATE_METRIC_TYPE_CIGP: +		return "CIGP"; +	case SRTE_CANDIDATE_METRIC_TYPE_CTE: +		return "CTE"; +	case SRTE_CANDIDATE_METRIC_TYPE_PIGP: +		return "PIGP"; +	case SRTE_CANDIDATE_METRIC_TYPE_PTE: +		return "PTE"; +	case SRTE_CANDIDATE_METRIC_TYPE_PHC: +		return "PHC"; +	case SRTE_CANDIDATE_METRIC_TYPE_MSD: +		return "MSD"; +	case SRTE_CANDIDATE_METRIC_TYPE_PD: +		return "PD"; +	case SRTE_CANDIDATE_METRIC_TYPE_PDV: +		return "PDV"; +	case SRTE_CANDIDATE_METRIC_TYPE_PL: +		return "PL"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPD: +		return "PPD"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPDV: +		return "PPDV"; +	case SRTE_CANDIDATE_METRIC_TYPE_PPL: +		return "PPL"; +	case SRTE_CANDIDATE_METRIC_TYPE_NAP: +		return "NAP"; +	case SRTE_CANDIDATE_METRIC_TYPE_NLP: +		return "NLP"; +	case SRTE_CANDIDATE_METRIC_TYPE_DC: +		return "DC"; +	case SRTE_CANDIDATE_METRIC_TYPE_BNC: +		return "BNC"; +	default: +		return "UNKNOWN"; +	} +} diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample new file mode 100644 index 0000000000..fc7acafc1f --- /dev/null +++ b/pathd/pathd.conf.sample @@ -0,0 +1,33 @@ +! Default pathd configuration sample +! +password frr +log stdout + +segment-routing + traffic-eng +  segment-list test1 +   index 10 mpls label 123 +   index 20 mpls label 456 +  ! +  segment-list test2 +   index 10 mpls label 321 +   index 20 mpls label 654 +  ! +  policy color 1 endpoint 1.1.1.1 +   name one +   binding-sid 100 +   candidate-path preference 100 name test1 explicit segment-list test1 +   candidate-path preference 200 name test2 explicit segment-list test2 +  ! +  policy color 2 endpoint 2.2.2.2 +   name two +   binding-sid 101 +   candidate-path preference 100 name def explicit segment-list test2 +   candidate-path preference 200 name dyn dynamic +    bandwidth 12345 +    metric bound abc 16 required +    metric te 10 +   ! +  ! + ! +! diff --git a/pathd/pathd.h b/pathd/pathd.h new file mode 100644 index 0000000000..4879239db8 --- /dev/null +++ b/pathd/pathd.h @@ -0,0 +1,412 @@ +/* + * 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_PATHD_H_ +#define _FRR_PATHD_H_ + +#include "lib/mpls.h" +#include "lib/ipaddr.h" +#include "lib/srte.h" +#include "lib/hook.h" + +enum srte_protocol_origin { +	SRTE_ORIGIN_UNDEFINED = 0, +	SRTE_ORIGIN_PCEP = 1, +	SRTE_ORIGIN_BGP = 2, +	SRTE_ORIGIN_LOCAL = 3, +}; + +enum srte_policy_status { +	SRTE_POLICY_STATUS_UNKNOWN = 0, +	SRTE_POLICY_STATUS_DOWN = 1, +	SRTE_POLICY_STATUS_UP = 2, +	SRTE_POLICY_STATUS_GOING_DOWN = 3, +	SRTE_POLICY_STATUS_GOING_UP = 4 +}; + +enum srte_candidate_type { +	SRTE_CANDIDATE_TYPE_UNDEFINED = 0, +	SRTE_CANDIDATE_TYPE_EXPLICIT = 1, +	SRTE_CANDIDATE_TYPE_DYNAMIC = 2, +}; + +enum srte_candidate_metric_type { +	/* IGP metric */ +	SRTE_CANDIDATE_METRIC_TYPE_IGP = 1, +	/* TE metric */ +	SRTE_CANDIDATE_METRIC_TYPE_TE = 2, +	/* Hop Counts */ +	SRTE_CANDIDATE_METRIC_TYPE_HC = 3, +	/* Aggregate bandwidth consumption */ +	SRTE_CANDIDATE_METRIC_TYPE_ABC = 4, +	/* Load of the most loaded link */ +	SRTE_CANDIDATE_METRIC_TYPE_LMLL = 5, +	/* Cumulative IGP cost */ +	SRTE_CANDIDATE_METRIC_TYPE_CIGP = 6, +	/* Cumulative TE cost */ +	SRTE_CANDIDATE_METRIC_TYPE_CTE = 7, +	/* P2MP IGP metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PIGP = 8, +	/* P2MP TE metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PTE = 9, +	/* P2MP hop count metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PHC = 10, +	/* Segment-ID (SID) Depth */ +	SRTE_CANDIDATE_METRIC_TYPE_MSD = 11, +	/* Path Delay metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PD = 12, +	/* Path Delay Variation metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PDV = 13, +	/* Path Loss metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PL = 14, +	/* P2MP Path Delay metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PPD = 15, +	/* P2MP Path Delay variation metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PPDV = 16, +	/* P2MP Path Loss metric */ +	SRTE_CANDIDATE_METRIC_TYPE_PPL = 17, +	/* Number of adaptations on a path */ +	SRTE_CANDIDATE_METRIC_TYPE_NAP = 18, +	/* Number of layers on a path */ +	SRTE_CANDIDATE_METRIC_TYPE_NLP = 19, +	/* Domain Count metric */ +	SRTE_CANDIDATE_METRIC_TYPE_DC = 20, +	/* Border Node Count metric */ +	SRTE_CANDIDATE_METRIC_TYPE_BNC = 21, +}; +#define MAX_METRIC_TYPE 21 + +enum srte_segment_nai_type { +	SRTE_SEGMENT_NAI_TYPE_NONE = 0, +	SRTE_SEGMENT_NAI_TYPE_IPV4_NODE = 1, +	SRTE_SEGMENT_NAI_TYPE_IPV6_NODE = 2, +	SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY = 3, +	SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY = 4, +	SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5 +}; + +enum objfun_type { +	OBJFUN_UNDEFINED = 0, +	/* Minimum Cost Path [RFC5541] */ +	OBJFUN_MCP = 1, +	/* Minimum Load Path [RFC5541] */ +	OBJFUN_MLP = 2, +	/* Maximum residual Bandwidth Path [RFC5541] */ +	OBJFUN_MBP = 3, +	/* Minimize aggregate Bandwidth Consumption [RFC5541] */ +	OBJFUN_MBC = 4, +	/* Minimize the Load of the most loaded Link [RFC5541] */ +	OBJFUN_MLL = 5, +	/* Minimize the Cumulative Cost of a set of paths [RFC5541] */ +	OBJFUN_MCC = 6, +	/* Shortest Path Tree [RFC8306] */ +	OBJFUN_SPT = 7, +	/* Minimum Cost Tree [RFC8306] */ +	OBJFUN_MCT = 8, +	/* Minimum Packet Loss Path [RFC8233] */ +	OBJFUN_MPLP = 9, +	/* Maximum Under-Utilized Path [RFC8233] */ +	OBJFUN_MUP = 10, +	/* Maximum Reserved Under-Utilized Path [RFC8233] */ +	OBJFUN_MRUP = 11, +	/* Minimize the number of Transit Domains [RFC8685] */ +	OBJFUN_MTD = 12, +	/* Minimize the number of Border Nodes [RFC8685] */ +	OBJFUN_MBN = 13, +	/* Minimize the number of Common Transit Domains [RFC8685] */ +	OBJFUN_MCTD = 14, +	/* Minimize the number of Shared Links [RFC8800] */ +	OBJFUN_MSL = 15, +	/* Minimize the number of Shared SRLGs [RFC8800] */ +	OBJFUN_MSS = 16, +	/* Minimize the number of Shared Nodes [RFC8800] */ +	OBJFUN_MSN = 17, +}; +#define MAX_OBJFUN_TYPE 17 + +enum affinity_filter_type { +	AFFINITY_FILTER_UNDEFINED = 0, +	AFFINITY_FILTER_EXCLUDE_ANY = 1, +	AFFINITY_FILTER_INCLUDE_ANY = 2, +	AFFINITY_FILTER_INCLUDE_ALL = 3, +}; +#define MAX_AFFINITY_FILTER_TYPE 3 + +struct srte_segment_list; + +struct srte_segment_entry { +	RB_ENTRY(srte_segment_entry) entry; + +	/* The segment list the entry belong to */ +	struct srte_segment_list *segment_list; + +	/* Index of the Label. */ +	uint32_t index; + +	/* Label Value. */ +	mpls_label_t sid_value; + +	/* NAI Type */ +	enum srte_segment_nai_type nai_type; +	/* NAI local address when nai type is not NONE */ +	struct ipaddr nai_local_addr; +	/* NAI local interface when nai type is not IPv4 unnumbered adjacency */ +	uint32_t nai_local_iface; +	/* NAI local interface when nai type is IPv4 or IPv6 adjacency */ +	struct ipaddr nai_remote_addr; +	/* NAI remote interface when nai type is not IPv4 unnumbered adjacency +	 */ +	uint32_t nai_remote_iface; +}; +RB_HEAD(srte_segment_entry_head, srte_segment_entry); +RB_PROTOTYPE(srte_segment_entry_head, srte_segment_entry, entry, +	     srte_segment_entry_compare) + +struct srte_segment_list { +	RB_ENTRY(srte_segment_list) entry; + +	/* Name of the Segment List. */ +	char name[64]; + +	/* The Protocol-Origin. */ +	enum srte_protocol_origin protocol_origin; + +	/* The Originator */ +	char originator[64]; + +	/* Nexthops. */ +	struct srte_segment_entry_head segments; + +	/* Status flags. */ +	uint16_t flags; +#define F_SEGMENT_LIST_NEW 0x0002 +#define F_SEGMENT_LIST_MODIFIED 0x0004 +#define F_SEGMENT_LIST_DELETED 0x0008 +}; +RB_HEAD(srte_segment_list_head, srte_segment_list); +RB_PROTOTYPE(srte_segment_list_head, srte_segment_list, entry, +	     srte_segment_list_compare) + +struct srte_metric { +	uint16_t flags; +#define F_METRIC_IS_DEFINED 0x0001 +#define F_METRIC_IS_REQUIRED 0x0002 +#define F_METRIC_IS_BOUND 0x0004 +#define F_METRIC_IS_COMPUTED 0x0008 +	float value; +}; + +/* Runtime information about the candidate path */ +struct srte_lsp { +	/* Backpointer to the Candidate Path. */ +	struct srte_candidate *candidate; + +	/* The associated Segment List. */ +	struct srte_segment_list *segment_list; + +	/* The Protocol-Origin. */ +	enum srte_protocol_origin protocol_origin; + +	/* The Originator */ +	char originator[64]; + +	/* The Discriminator */ +	uint32_t discriminator; + +	/* Flags. */ +	uint32_t flags; + +	/* Metrics LSP Values */ +	struct srte_metric metrics[MAX_METRIC_TYPE]; + +	/* Bandwidth Configured Value */ +	float bandwidth; + +	/* The objective function in used */ +	enum objfun_type objfun; +}; + +/* Configured candidate path */ +struct srte_candidate { +	RB_ENTRY(srte_candidate) entry; + +	/* Backpointer to SR Policy */ +	struct srte_policy *policy; + +	/* The LSP associated with this candidate path. */ +	struct srte_lsp *lsp; + +	/* Administrative preference. */ +	uint32_t preference; + +	/* Symbolic Name. */ +	char name[64]; + +	/* The associated Segment List. */ +	struct srte_segment_list *segment_list; + +	/* The Protocol-Origin. */ +	enum srte_protocol_origin protocol_origin; + +	/* The Originator */ +	char originator[64]; + +	/* The Discriminator */ +	uint32_t discriminator; + +	/* The Type (explicit or dynamic) */ +	enum srte_candidate_type type; + +	/* Flags. */ +	uint32_t flags; +#define F_CANDIDATE_BEST 0x0001 +#define F_CANDIDATE_NEW 0x0002 +#define F_CANDIDATE_MODIFIED 0x0004 +#define F_CANDIDATE_DELETED 0x0008 +#define F_CANDIDATE_HAS_BANDWIDTH 0x0100 +#define F_CANDIDATE_REQUIRED_BANDWIDTH 0x0200 +#define F_CANDIDATE_HAS_OBJFUN 0x0400 +#define F_CANDIDATE_REQUIRED_OBJFUN 0x0800 +#define F_CANDIDATE_HAS_EXCLUDE_ANY 0x1000 +#define F_CANDIDATE_HAS_INCLUDE_ANY 0x2000 +#define F_CANDIDATE_HAS_INCLUDE_ALL 0x4000 + +	/* Metrics Configured Values */ +	struct srte_metric metrics[MAX_METRIC_TYPE]; + +	/* Bandwidth Configured Value */ +	float bandwidth; + +	/* Configured objective functions */ +	enum objfun_type objfun; + +	/* Path constraints attribute filters */ +	uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE]; + +	/* Hooks delaying timer */ +	struct thread *hook_timer; +}; + +RB_HEAD(srte_candidate_head, srte_candidate); +RB_PROTOTYPE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare) + +struct srte_policy { +	RB_ENTRY(srte_policy) entry; + +	/* Color */ +	uint32_t color; + +	/* Endpoint */ +	struct ipaddr endpoint; + +	/* Name */ +	char name[64]; + +	/* Binding SID */ +	mpls_label_t binding_sid; + +	/* Operational Status of the policy */ +	enum srte_policy_status status; + +	/* Best candidate path. */ +	struct srte_candidate *best_candidate; + +	/* Candidate Paths */ +	struct srte_candidate_head candidate_paths; +	/* Status flags. */ +	uint16_t flags; +#define F_POLICY_NEW 0x0002 +#define F_POLICY_MODIFIED 0x0004 +#define F_POLICY_DELETED 0x0008 +}; +RB_HEAD(srte_policy_head, srte_policy); +RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare) + +DECLARE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate), +	     (candidate)) +DECLARE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate), +	     (candidate)) +DECLARE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate), +	     (candidate)) + +extern struct srte_segment_list_head srte_segment_lists; +extern struct srte_policy_head srte_policies; +extern struct zebra_privs_t pathd_privs; + +/* master thread, defined in path_main.c */ +extern struct thread_master *master; + +/* pathd.c */ +struct srte_segment_list *srte_segment_list_add(const char *name); +void srte_segment_list_del(struct srte_segment_list *segment_list); +struct srte_segment_list *srte_segment_list_find(const char *name); +struct srte_segment_entry * +srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index); +void srte_segment_entry_del(struct srte_segment_entry *segment); +void srte_segment_entry_set_nai(struct srte_segment_entry *segment, +				enum srte_segment_nai_type type, +				struct ipaddr *local_ip, uint32_t local_iface, +				struct ipaddr *remote_ip, +				uint32_t remote_iface); +struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint); +void srte_policy_del(struct srte_policy *policy); +struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint); +void srte_policy_update_binding_sid(struct srte_policy *policy, +				    uint32_t binding_sid); +void srte_apply_changes(void); +void srte_policy_apply_changes(struct srte_policy *policy); +struct srte_candidate *srte_candidate_add(struct srte_policy *policy, +					  uint32_t preference); +void srte_candidate_del(struct srte_candidate *candidate); +void srte_candidate_set_bandwidth(struct srte_candidate *candidate, +				  float bandwidth, bool required); +void srte_candidate_unset_bandwidth(struct srte_candidate *candidate); +void srte_candidate_set_metric(struct srte_candidate *candidate, +			       enum srte_candidate_metric_type type, +			       float value, bool required, bool is_cound, +			       bool is_computed); +void srte_candidate_unset_metric(struct srte_candidate *candidate, +				 enum srte_candidate_metric_type type); +void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required, +			       enum objfun_type type); +void srte_candidate_unset_objfun(struct srte_candidate *candidate); +void srte_candidate_set_affinity_filter(struct srte_candidate *candidate, +					enum affinity_filter_type type, +					uint32_t filter); +void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate, +					  enum affinity_filter_type type); +void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth, +			    bool required); +void srte_lsp_unset_bandwidth(struct srte_lsp *lsp); +void srte_lsp_set_metric(struct srte_lsp *lsp, +			 enum srte_candidate_metric_type type, float value, +			 bool required, bool is_cound, bool is_computed); +void srte_lsp_unset_metric(struct srte_lsp *lsp, +			   enum srte_candidate_metric_type type); +struct srte_candidate *srte_candidate_find(struct srte_policy *policy, +					   uint32_t preference); +struct srte_segment_entry * +srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index); +void srte_candidate_status_update(struct srte_candidate *candidate, int status); +void srte_candidate_unset_segment_list(const char *originator, bool force); +const char *srte_origin2str(enum srte_protocol_origin origin); + +/* path_cli.c */ +void path_cli_init(void); + +#endif /* _FRR_PATHD_H_ */ diff --git a/pathd/subdir.am b/pathd/subdir.am new file mode 100644 index 0000000000..23399b9d81 --- /dev/null +++ b/pathd/subdir.am @@ -0,0 +1,48 @@ +# +# pathd +# + +if PATHD +noinst_LIBRARIES += pathd/libpath.a +sbin_PROGRAMS += pathd/pathd +dist_examples_DATA += pathd/pathd.conf.sample +vtysh_scan += $(top_srcdir)/pathd/path_cli.c +vtysh_daemons += pathd +# TODO add man page +#man8 += $(MANBUILD)/pathd.8 +endif + +pathd_libpath_a_SOURCES = \ +	pathd/path_cli.c \ +	pathd/path_debug.c \ +	pathd/path_errors.c \ +	pathd/path_main.c \ +	pathd/path_memory.c \ +	pathd/path_nb.c \ +	pathd/path_nb_config.c \ +	pathd/path_nb_state.c \ +	pathd/path_zebra.c \ +	pathd/pathd.c \ +	# end + +clippy_scan += \ +	pathd/path_cli.c \ +	# end + +noinst_HEADERS += \ +	pathd/path_debug.h \ +	pathd/path_errors.h \ +	pathd/path_memory.h \ +	pathd/path_nb.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) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 4ebc504b8a..02c272f47c 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -27,6 +27,7 @@  %{!?with_vrrpd:         %global  with_vrrpd         1 }  %{!?with_rtadv:         %global  with_rtadv         1 }  %{!?with_watchfrr:      %global  with_watchfrr      1 } +%{!?with_pathd:         %global  with_pathd         1 }  # user and group  %{!?frr_user:           %global  frr_user           frr } @@ -87,7 +88,7 @@  %{!?frr_gid:            %global  frr_gid            92 }  %{!?vty_gid:            %global  vty_gid            85 } -%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd fabricd +%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd staticd bfdd fabricd pathd  %if %{with_ldpd}      %define daemon_ldpd ldpd @@ -143,7 +144,13 @@      %define daemon_bfdd ""  %endif -%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} +%if %{with_pathd} +    %define daemon_pathd pathd +%else +    %define daemon_pathd "" +%endif + +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} %{daemon_pathd}  #release sub-revision (the two digits after the CONFDATE)  %{!?release_rev:        %global  release_rev        01 } @@ -398,6 +405,11 @@ routing state through standard SNMP MIBs.  %else      --disable-bfdd \  %endif +%if %{with_pathd} +    --enable-pathd \ +%else +    --disable-pathd \ +%endif      --enable-snmp      # end @@ -526,6 +538,9 @@ zebra_spec_add_service fabricd	2618/tcp "Fabricd vty"  %if %{with_vrrpd}      zebra_spec_add_service vrrpd    2619/tcp "VRRPd vty"  %endif +%if %{with_pathd} +    zebra_spec_add_service pathd    2620/tcp "Pathd vty" +%endif  %if "%{initsystem}" == "systemd"      for daemon in %all_daemons ; do @@ -681,6 +696,9 @@ fi  %if %{with_bfdd}      %{_sbindir}/bfdd  %endif +%if %{with_pathd} +    %{_sbindir}/pathd +%endif  %{_libdir}/libfrr.so*  %{_libdir}/libfrrcares*  %{_libdir}/libfrrospf* @@ -798,6 +816,9 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons  -    migrate route-maps to use northbound interface  - plus countless bug fixes and other improvements +* Mon Jun 15 2020 Sascha Kattelmann <sascha@netdef.org> +- Add Pathd support +  * Wed May 06 2020 David Lamparter <equinox@opensourcerouting.org> - 7.3.1  - upstream 7.3.1 diff --git a/tests/topotests/isis-sr-te-topo1/dst/zebra.conf b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf new file mode 100644 index 0000000000..e873ac8a5c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/dst/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname dst +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 9.9.9.2/32 + ipv6 address 2001:db8:1066::2/128 +! +interface eth-rt6 + ip address 10.0.11.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf new file mode 100644 index 0000000000..efc03701b5 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/bgpd.conf @@ -0,0 +1,16 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 1.1.1.1 + neighbor 6.6.6.6 remote-as 1 + neighbor 6.6.6.6 update-source lo + ! + address-family ipv4 unicast +  redistribute static +  neighbor 6.6.6.6 next-hop-self +  neighbor 6.6.6.6 route-map SET_SR_POLICY in + exit-address-family +! +route-map SET_SR_POLICY permit 10 + set sr-te color 1 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..70ae1b07f5 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/isisd.conf @@ -0,0 +1,30 @@ +password 1 +hostname rt1 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 + segment-routing prefix 2001:db8:1000::1/128 index 11 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf new file mode 100644 index 0000000000..911971496e --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/pathd.conf @@ -0,0 +1,21 @@ +log file pathd.log +! +hostname rt1 +! +segment-routing + traffic-eng +  segment-list default +   index 10 mpls label 16050 +   index 20 mpls label 16060 +  ! +  segment-list test +   index 10 mpls label 16020 +   index 20 mpls label 16040 +   index 30 mpls label 16060 +  ! +  policy color 1 endpoint 6.6.6.6 +   name default +   binding-sid 1111 +  ! + ! +!
\ No newline at end of file diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 0000000000..d4b27d157d --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,91 @@ +{ +  "1111":{ +    "inLabel":1111, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16050, +        "outLabelStack":[ +          16050, +          16060 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      } +    ] +  }, +  "16020":{ +    "inLabel":16020, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16020, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  }, +  "16030":{ +    "inLabel":16030, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16030, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      } +    ] +  }, +  "16040":{ +    "inLabel":16040, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16040, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  }, +  "16050":{ +    "inLabel":16050, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16050, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      } +    ] +  }, +  "16060":{ +    "inLabel":16060, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16060, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      }, +      { +        "type":"SR (IS-IS)", +        "outLabel":16060, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 0000000000..5fe58d0824 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,74 @@ +{ +  "16020":{ +    "inLabel":16020, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16020, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  }, +  "16030":{ +    "inLabel":16030, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16030, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      } +    ] +  }, +  "16040":{ +    "inLabel":16040, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16040, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  }, +  "16050":{ +    "inLabel":16050, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16050, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      } +    ] +  }, +  "16060":{ +    "inLabel":16060, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16060, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.3" +      }, +      { +        "type":"SR (IS-IS)", +        "outLabel":16060, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref new file mode 100644 index 0000000000..4ef8d946f2 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "6.6.6.6", +          "is-operational": false +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref new file mode 100644 index 0000000000..9b28f6a42b --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,20 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "6.6.6.6", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "discriminator": "*", +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 0000000000..9b28f6a42b --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,20 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "6.6.6.6", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "discriminator": "*", +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 0000000000..249117198a --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,25 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "6.6.6.6", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "discriminator": "*", +              "is-best-candidate-path": false +            }, +            { +              "preference": 200, +              "discriminator": "*", +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 0000000000..21f71f1254 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,20 @@ +{ +  "1111":{ +    "inLabel":1111, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16020, +        "outLabelStack":[ +          16020, +          16040, +          16060 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref new file mode 100644 index 0000000000..3635c89efb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_add_segment.ref @@ -0,0 +1,21 @@ +{ +  "1111":{ +    "inLabel":1111, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16020, +        "outLabelStack":[ +          16020, +          16040, +          16050, +          16060 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref new file mode 100644 index 0000000000..5712d210d4 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step4/show_mpls_table_change_segment.ref @@ -0,0 +1,21 @@ +{ +  "1111":{ +    "inLabel":1111, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16020, +        "outLabelStack":[ +          16020, +          16040, +          16030, +          16060 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.1.2" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref new file mode 100644 index 0000000000..5a76246e50 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_active_srte.ref @@ -0,0 +1,29 @@ +{ +  "9.9.9.2\/32":[ +    { +      "prefix":"9.9.9.2\/32", +      "protocol":"bgp", +      "installed":true, +      "nexthops":[ +        { +          "ip":"6.6.6.6", +          "afi":"ipv4", +          "active":true, +          "recursive":true, +          "srteColor":1 +        }, +        { +          "fib":true, +          "ip":"10.0.1.3", +          "afi":"ipv4", +          "interfaceName":"eth-sw1", +          "active":true, +          "labels":[ +            16050, +            16060 +          ] +        } +      ] +    } +  ] +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref new file mode 100644 index 0000000000..09d5958305 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref @@ -0,0 +1,38 @@ +{ +  "9.9.9.2\/32":[ +    { +      "prefix":"9.9.9.2\/32", +      "protocol":"bgp", +      "installed":true, +      "nexthops":[ +        { +          "ip":"6.6.6.6", +          "afi":"ipv4", +          "active":true, +          "recursive":true, +          "srteColor":1 +        }, +        { +          "fib":true, +          "ip":"10.0.1.2", +          "afi":"ipv4", +          "interfaceName":"eth-sw1", +          "active":true, +          "labels":[ +            16060 +          ] +        }, +        { +          "fib":true, +          "ip":"10.0.1.3", +          "afi":"ipv4", +          "interfaceName":"eth-sw1", +          "active":true, +          "labels":[ +            16060 +          ] +        } +      ] +    } +  ] +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref new file mode 100644 index 0000000000..e26039b835 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_active.ref @@ -0,0 +1,20 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +       { +         "color": 1, +         "endpoint": "6.6.6.6", +         "is-operational": true, +         "candidate-path": [ +           { +             "preference": 100, +             "discriminator": "*", +             "is-best-candidate-path": true +           } +         ] +       } +     ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref new file mode 100644 index 0000000000..01505c0318 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/step5/show_operational_data_inactive.ref @@ -0,0 +1,20 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "6.6.6.6", +          "is-operational": false, +          "candidate-path": [ +            { +              "preference": 100, +              "discriminator": "*", +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..9d71d3005f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt1/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-sw1 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..733f26bc62 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt2/isisd.conf @@ -0,0 +1,41 @@ +hostname rt2 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 no-php-flag + segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..dcb0686dc2 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt2/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-sw1 + ip address 10.0.1.2/24 +! +interface eth-rt4-1 + ip address 10.0.2.2/24 +! +interface eth-rt4-2 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..2395906cbf --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt3/isisd.conf @@ -0,0 +1,41 @@ +hostname rt3 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 no-php-flag + segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..3254529386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt3/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-sw1 + ip address 10.0.1.3/24 +! +interface eth-rt5-1 + ip address 10.0.4.3/24 +! +interface eth-rt5-2 + ip address 10.0.5.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..07a7867cbb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt4/isisd.conf @@ -0,0 +1,48 @@ +hostname rt4 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt2-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 no-php-flag + segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..4945897e9d --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt4/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt2-1 + ip address 10.0.2.4/24 +! +interface eth-rt2-2 + ip address 10.0.3.4/24 +! +interface eth-rt5 + ip address 10.0.6.4/24 +! +interface eth-rt6 + ip address 10.0.7.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..b0fcdede07 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt5/isisd.conf @@ -0,0 +1,48 @@ +hostname rt5 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt3-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 no-php-flag + segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag +! diff --git a/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..4cfea1a59f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt5/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt3-1 + ip address 10.0.4.5/24 +! +interface eth-rt3-2 + ip address 10.0.5.5/24 +! +interface eth-rt4 + ip address 10.0.6.5/24 +! +interface eth-rt6 + ip address 10.0.8.5/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf new file mode 100644 index 0000000000..e72ee52fce --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/bgpd.conf @@ -0,0 +1,12 @@ +log file bgpd.log +! +router bgp 1 + bgp router-id 6.6.6.6 + neighbor 1.1.1.1 remote-as 1 + neighbor 1.1.1.1 update-source lo + ! + address-family ipv4 unicast +  redistribute static +  neighbor 1.1.1.1 next-hop-self + exit-address-family +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf new file mode 100644 index 0000000000..3be24ad24c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/isisd.conf @@ -0,0 +1,36 @@ +hostname rt6 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 + segment-routing prefix 2001:db8:1000::6/128 index 61 +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf new file mode 100644 index 0000000000..3bada7147c --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/pathd.conf @@ -0,0 +1,21 @@ +log file pathd.log +! +hostname rt6 +! +segment-routing + traffic-eng +  segment-list default +   index 10 mpls label 16020 +   index 20 mpls label 16010 +  ! +  segment-list test +   index 10 mpls label 16050 +   index 20 mpls label 16030 +   index 30 mpls label 16010 +  ! +  policy color 1 endpoint 1.1.1.1 +   name default +   binding-sid 6666 +  ! + ! +! diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref new file mode 100644 index 0000000000..2bb000346f --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_with_candidate.ref @@ -0,0 +1,91 @@ +{ +  "6666":{ +    "inLabel":6666, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16020, +        "outLabelStack":[ +          16020, +          16010 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.7.4" +      } +    ] +  }, +  "16010": { +      "inLabel": 16010, +      "installed": true, +      "nexthops": [ +          { +              "distance": 150, +              "installed": true, +              "nexthop": "10.0.7.4", +              "outLabel": 16010, +              "type": "SR (IS-IS)" +          }, +          { +              "distance": 150, +              "installed": true, +              "nexthop": "10.0.8.5", +              "outLabel": 16010, +              "type": "SR (IS-IS)" +          } +      ] +  }, +  "16020":{ +    "inLabel":16020, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16020, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.7.4" +      } +    ] +  }, +  "16030":{ +    "inLabel":16030, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16030, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.8.5" +      } +    ] +  }, +  "16040":{ +    "inLabel":16040, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16040, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.7.4" +      } +    ] +  }, +  "16050":{ +    "inLabel":16050, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16050, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.8.5" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref new file mode 100644 index 0000000000..348f7761eb --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step1/show_mpls_table_without_candidate.ref @@ -0,0 +1,74 @@ +{ +  "16010": { +      "inLabel": 16010, +      "installed": true, +      "nexthops": [ +          { +              "distance": 150, +              "installed": true, +              "nexthop": "10.0.7.4", +              "outLabel": 16010, +              "type": "SR (IS-IS)" +          }, +          { +              "distance": 150, +              "installed": true, +              "nexthop": "10.0.8.5", +              "outLabel": 16010, +              "type": "SR (IS-IS)" +          } +      ] +  }, +  "16020":{ +    "inLabel":16020, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16020, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.7.4" +      } +    ] +  }, +  "16030":{ +    "inLabel":16030, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16030, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.8.5" +      } +    ] +  }, +  "16040":{ +    "inLabel":16040, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16040, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.7.4" +      } +    ] +  }, +  "16050":{ +    "inLabel":16050, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR (IS-IS)", +        "outLabel":16050, +        "distance":150, +        "installed":true, +        "nexthop":"10.0.8.5" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref new file mode 100644 index 0000000000..241c80bdd7 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data.ref @@ -0,0 +1,13 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +        "policy": [ +        { +          "color": 1, +          "endpoint": "1.1.1.1", +          "is-operational": false +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref new file mode 100644 index 0000000000..20ea69e386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step2/show_operational_data_with_candidate.ref @@ -0,0 +1,19 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "1.1.1.1", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref new file mode 100644 index 0000000000..20ea69e386 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_single_candidate.ref @@ -0,0 +1,19 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "1.1.1.1", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref new file mode 100644 index 0000000000..10cafe9091 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step3/show_operational_data_with_two_candidates.ref @@ -0,0 +1,23 @@ +{ +  "frr-pathd:pathd": { +    "srte": { +      "policy": [ +        { +          "color": 1, +          "endpoint": "1.1.1.1", +          "is-operational": true, +          "candidate-path": [ +            { +              "preference": 100, +              "is-best-candidate-path": false +            }, +            { +              "preference": 200, +              "is-best-candidate-path": true +            } +          ] +        } +      ] +    } +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref new file mode 100644 index 0000000000..95bf995e2e --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/step4/show_mpls_table.ref @@ -0,0 +1,20 @@ +{ +  "6666":{ +    "inLabel":6666, +    "installed":true, +    "nexthops":[ +      { +        "type":"SR-TE", +        "outLabel":16050, +        "outLabelStack":[ +          16050, +          16030, +          16010 +        ], +        "distance":150, +        "installed":true, +        "nexthop":"10.0.8.5" +      } +    ] +  } +} diff --git a/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf new file mode 100644 index 0000000000..32c6e6c4e0 --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/rt6/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt4 + ip address 10.0.7.6/24 +! +interface eth-rt5 + ip address 10.0.8.6/24 +! +interface eth-dst + ip address 10.0.11.1/24 +! +ip forwarding +! +ip route 9.9.9.2/32 10.0.11.2 +! +line vty +! diff --git a/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py new file mode 100755 index 0000000000..5e5c0f9fee --- /dev/null +++ b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python + +# +# test_isis_sr_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_isis_sr_te_topo1.py: + +                         +---------+ +                         |         | +                         |   RT1   | +                         | 1.1.1.1 | +                         |         | +                         +---------+ +                              |eth-sw1 +                              | +                              | +                              | +         +---------+          |          +---------+ +         |         |          |          |         | +         |   RT2   |eth-sw1   |   eth-sw1|   RT3   | +         | 2.2.2.2 +----------+----------+ 3.3.3.3 | +         |         |     10.0.1.0/24     |         | +         +---------+                     +---------+ +    eth-rt4-1|  |eth-rt4-2          eth-rt5-1|  |eth-rt5-2 +             |  |                            |  | +  10.0.2.0/24|  |10.0.3.0/24      10.0.4.0/24|  |10.0.5.0/24 +             |  |                            |  | +    eth-rt2-1|  |eth-rt2-2          eth-rt3-1|  |eth-rt3-2 +         +---------+                     +---------+ +         |         |                     |         | +         |   RT4   |     10.0.6.0/24     |   RT5   | +         | 4.4.4.4 +---------------------+ 5.5.5.5 | +         |         |eth-rt5       eth-rt4|         | +         +---------+                     +---------+ +       eth-rt6|                                |eth-rt6 +              |                                | +   10.0.7.0/24|                                |10.0.8.0/24 +              |          +---------+           | +              |          |         |           | +              |          |   RT6   |           | +              +----------+ 6.6.6.6 +-----------+ +                  eth-rt4|         |eth-rt5 +                         +---------+ +                              |eth-dst (.1) +                              | +                              |10.0.11.0/24 +                              | +                              |eth-rt6 (.2) +                         +---------+ +                         |         | +                         |   DST   | +                         | 9.9.9.2 | +                         |         | +                         +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +class TemplateTopo(Topo): +    "Test topology builder" +    def build(self, *_args, **_opts): +        "Build function" +        tgen = get_topogen(self) + +        # +        # Define FRR Routers +        # +        for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6', 'dst']: +            tgen.add_router(router) + +        # +        # Define connections +        # +        switch = tgen.add_switch('s1') +        switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1") +        switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1") +        switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1") + +        switch = tgen.add_switch('s2') +        switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-1") +        switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-1") + +        switch = tgen.add_switch('s3') +        switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-2") +        switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-2") + +        switch = tgen.add_switch('s4') +        switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-1") +        switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-1") + +        switch = tgen.add_switch('s5') +        switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-2") +        switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-2") + +        switch = tgen.add_switch('s6') +        switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5") +        switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4") + +        switch = tgen.add_switch('s7') +        switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6") +        switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4") + +        switch = tgen.add_switch('s8') +        switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6") +        switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5") + +        switch = tgen.add_switch('s9') +        switch.add_link(tgen.gears['rt6'], nodeif="eth-dst") +        switch.add_link(tgen.gears['dst'], nodeif="eth-rt6") + +def setup_module(mod): +    "Sets up the pytest environment" +    tgen = Topogen(TemplateTopo, mod.__name__) +    tgen.start_topology() + +    router_list = tgen.routers() + +    # For all registered routers, load the zebra configuration file +    for rname, router in router_list.iteritems(): +        router.load_config( +            TopoRouter.RD_ZEBRA, +            os.path.join(CWD, '{}/zebra.conf'.format(rname)) +        ) +        router.load_config( +            TopoRouter.RD_ISIS, +            os.path.join(CWD, '{}/isisd.conf'.format(rname)) +        ) +        router.load_config( +            TopoRouter.RD_PATH, +            os.path.join(CWD, '{}/pathd.conf'.format(rname)) +        ) +        router.load_config( +            TopoRouter.RD_BGP, +            os.path.join(CWD, '{}/bgpd.conf'.format(rname)) +        ) + +    tgen.start_router() + +def teardown_module(mod): +    "Teardown the pytest environment" +    tgen = get_topogen() + +    # This function tears down the whole topology. +    tgen.stop_topology() + +def setup_testcase(msg): +    logger.info(msg) +    tgen = get_topogen() + +    # Skip if previous fatal error condition is raised +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    return tgen + +def print_cmd_result(rname, command): +    print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + +def compare_json_test(router, command, reference, exact): +    output = router.vtysh_cmd(command, isjson=True) +    result = topotest.json_cmp(output, reference) + +    # Note: topotest.json_cmp() just checks on inclusion of keys. +    # For exact matching also compare the other way around. +    if not result and exact: +       return topotest.json_cmp(reference, output) +    else: +       return result + +def cmp_json_output(rname, command, reference, exact=False): +    "Compare router JSON output" + +    logger.info('Comparing router "%s" "%s" output', rname, command) + +    tgen = get_topogen() +    filename = '{}/{}/{}'.format(CWD, rname, reference) +    expected = json.loads(open(filename).read()) + +    # Run test function until we get an result. Wait at most 60 seconds. +    test_func = partial(compare_json_test, +        tgen.gears[rname], command, expected, exact) +    _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) +    assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) +    assert diff is None, assertmsg + +def cmp_json_output_exact(rname, command, reference): +    return cmp_json_output(rname, command, reference, True) + +def add_candidate_path(rname, endpoint, pref, name, segment_list='default'): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "policy color 1 endpoint ''' + endpoint + '''" \ +              -c "candidate-path preference ''' + str(pref) + ''' name ''' + name + ''' explicit segment-list ''' + segment_list + '''"''') + +def delete_candidate_path(rname, endpoint, pref): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "policy color 1 endpoint ''' + endpoint + '''" \ +              -c "no candidate-path preference ''' + str(pref) + '''"''') + +def add_segment(rname, name, index, label): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "segment-list ''' + name + '''" \ +              -c "index ''' + str(index) + ''' mpls label ''' + str(label) + '''"''') + +def delete_segment(rname, name, index): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "segment-list ''' + name + '''" \ +              -c "no index ''' + str(index) + '''"''') + +def create_sr_policy(rname, endpoint, bsid): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "policy color 1 endpoint ''' + endpoint + '''" \ +              -c "name default" \ +              -c "binding-sid ''' + str(bsid) + '''"''') + +def delete_sr_policy(rname, endpoint): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "segment-routing" \ +              -c "traffic-eng" \ +              -c "no policy color 1 endpoint ''' + endpoint + '''"''') + +def create_prefix_sid(rname, prefix, sid): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "router isis 1" \ +              -c "segment-routing prefix ''' + prefix + " index " + str(sid) + '''"''') + +def delete_prefix_sid(rname, prefix): +    get_topogen().net[rname].cmd(''' \ +        vtysh -c "conf t" \ +              -c "router isis 1" \ +              -c "no segment-routing prefix "''' + prefix) + +# +# Step 1 +# +# Checking the MPLS table using a single SR Policy and a single Candidate Path +# +def test_srte_init_step1(): +    setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution") + +    for rname in ['rt1', 'rt6']: +        cmp_json_output(rname, +                        "show mpls table json", +                        "step1/show_mpls_table_without_candidate.ref") + +def test_srte_add_candidate_check_mpls_table_step1(): +    setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        add_candidate_path(rname, endpoint, 100, 'default') +        cmp_json_output(rname, +                        "show mpls table json", +                        "step1/show_mpls_table_with_candidate.ref") +        delete_candidate_path(rname, endpoint, 100) + +def test_srte_reinstall_sr_policy_check_mpls_table_step1(): +    setup_testcase("Test (step 1): check MPLS table after the SR Policy was removed and reinstalled") + +    for rname, endpoint, bsid in [('rt1', '6.6.6.6', 1111), ('rt6', '1.1.1.1', 6666)]: +        add_candidate_path(rname, endpoint, 100, 'default') +        delete_sr_policy(rname, endpoint) +        cmp_json_output(rname, +                        "show mpls table json", +                        "step1/show_mpls_table_without_candidate.ref") +        create_sr_policy(rname, endpoint, bsid) +        add_candidate_path(rname, endpoint, 100, 'default') +        cmp_json_output(rname, +                        "show mpls table json", +                        "step1/show_mpls_table_with_candidate.ref") +        delete_candidate_path(rname, endpoint, 100) + +# +# Step 2 +# +# Checking pathd operational data using a single SR Policy and a single Candidate Path +# +def test_srte_bare_policy_step2(): +    setup_testcase("Test (step 2): bare SR Policy should not be operational") + +    for rname in ['rt1', 'rt6']: +        cmp_json_output_exact(rname, +                              "show yang operational-data /frr-pathd:pathd pathd", +                              "step2/show_operational_data.ref") + +def test_srte_add_candidate_check_operational_data_step2(): +    setup_testcase("Test (step 2): add single Candidate Path, SR Policy should be operational") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        add_candidate_path(rname, endpoint, 100, 'default') +        cmp_json_output(rname, +                        "show yang operational-data /frr-pathd:pathd pathd", +                        "step2/show_operational_data_with_candidate.ref") + +def test_srte_config_remove_candidate_check_operational_data_step2(): +    setup_testcase("Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        delete_candidate_path(rname, endpoint, 100) +        cmp_json_output_exact(rname, +                              "show yang operational-data /frr-pathd:pathd pathd", +                              "step2/show_operational_data.ref") + +# +# Step 3 +# +# Testing the Candidate Path selection +# +def test_srte_add_two_candidates_step3(): +    setup_testcase("Test (step 3): second Candidate Path has higher Priority") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        for pref, cand_name in [('100', 'first'), ('200', 'second')]: +            add_candidate_path(rname, endpoint, pref, cand_name) +        cmp_json_output(rname, +                        "show yang operational-data /frr-pathd:pathd pathd", +                        "step3/show_operational_data_with_two_candidates.ref") + +    # cleanup +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        for pref in ['100', '200']: +            delete_candidate_path(rname, endpoint, pref) + +def test_srte_add_two_candidates_with_reverse_priority_step3(): +    setup_testcase("Test (step 3): second Candidate Path has lower Priority") + +    # Use reversed priorities here +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        for pref, cand_name in [('200', 'first'), ('100', 'second')]: +            add_candidate_path(rname, endpoint, pref, cand_name) +        cmp_json_output(rname, +                        "show yang operational-data /frr-pathd:pathd pathd", +                        "step3/show_operational_data_with_two_candidates.ref") + +    # cleanup +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        for pref in ['100', '200']: +            delete_candidate_path(rname, endpoint, pref) + +def test_srte_remove_best_candidate_step3(): +    setup_testcase("Test (step 3): delete the Candidate Path with higher priority") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        for pref, cand_name in [('100', 'first'), ('200', 'second')]: +            add_candidate_path(rname, endpoint, pref, cand_name) + +    # Delete candidate with higher priority +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        delete_candidate_path(rname, endpoint, 200) + +    # Candidate with lower priority should get active now +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        cmp_json_output(rname, +                        "show yang operational-data /frr-pathd:pathd pathd", +                        "step3/show_operational_data_with_single_candidate.ref") +        # cleanup +        delete_candidate_path(rname, endpoint, 100) + +# +# Step 4 +# +# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications +# +def test_srte_change_segment_list_check_mpls_table_step4(): +    setup_testcase("Test (step 4): check MPLS table for changed Segment List") + +    for rname, endpoint in [('rt1', '6.6.6.6'), ('rt6', '1.1.1.1')]: +        add_candidate_path(rname, endpoint, 100, 'default') +	# now change the segment list name +        add_candidate_path(rname, endpoint, 100, 'default', 'test') +        cmp_json_output(rname, +                        "show mpls table json", +                        "step4/show_mpls_table.ref") +        delete_candidate_path(rname, endpoint, 100) + +def test_srte_segment_list_add_segment_check_mpls_table_step4(): +    setup_testcase("Test (step 4): check MPLS table for added (then changed and finally deleted) segment") + +    add_candidate_path('rt1', '6.6.6.6', 100, 'default', 'test') + +    # first add a new segment +    add_segment('rt1', 'test', 25, 16050) +    cmp_json_output('rt1', +                    "show mpls table json", +                    "step4/show_mpls_table_add_segment.ref") + +    # ... then change it ... +    add_segment('rt1', 'test', 25, 16030) +    cmp_json_output('rt1', +                    "show mpls table json", +                    "step4/show_mpls_table_change_segment.ref") + +    # ... and finally delete it +    delete_segment('rt1', 'test', 25) +    cmp_json_output('rt1', +                    "show mpls table json", +                    "step4/show_mpls_table.ref") +    delete_candidate_path('rt1', '6.6.6.6', 100) + +# +# Step 5 +# +# Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map +# +def test_srte_route_map_with_sr_policy_check_nextop_step5(): +    setup_testcase("Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map") + +    # (re-)build the SR Policy two times to ensure that reinstalling still works +    for i in [1,2]: +        cmp_json_output('rt1', +                        "show ip route bgp json", +                        "step5/show_ip_route_bgp_inactive_srte.ref") + +        delete_sr_policy('rt1', '6.6.6.6') +        cmp_json_output('rt1', +                        "show ip route bgp json", +                        "step5/show_ip_route_bgp_inactive_srte.ref") + +        create_sr_policy('rt1', '6.6.6.6', 1111) +        cmp_json_output('rt1', +                        "show ip route bgp json", +                        "step5/show_ip_route_bgp_inactive_srte.ref") + +        add_candidate_path('rt1', '6.6.6.6', 100, 'default') +        cmp_json_output('rt1', +                        "show ip route bgp json", +                        "step5/show_ip_route_bgp_active_srte.ref") + +        delete_candidate_path('rt1', '6.6.6.6', 100) + +def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5(): +    setup_testcase("Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity") + +    # first add a candidate path so the SR Policy is active +    add_candidate_path('rt1', '6.6.6.6', 100, 'default') +    cmp_json_output('rt1', +                    "show yang operational-data /frr-pathd:pathd pathd", +                    "step5/show_operational_data_active.ref") + +    # delete prefix SID from first element of the configured path and check +    # if the SR Policy is inactive since the label can't be resolved anymore +    delete_prefix_sid('rt5', "5.5.5.5/32") +    cmp_json_output('rt1', +                    "show yang operational-data /frr-pathd:pathd pathd", +                    "step5/show_operational_data_inactive.ref") +    cmp_json_output('rt1', +                    "show ip route bgp json", +                    "step5/show_ip_route_bgp_inactive_srte.ref") + +    # re-create the prefix SID and check if the SR Policy is active +    create_prefix_sid('rt5', "5.5.5.5/32", 50) +    cmp_json_output('rt1', +                    "show yang operational-data /frr-pathd:pathd pathd", +                    "step5/show_operational_data_active.ref") +    cmp_json_output('rt1', +                    "show ip route bgp json", +                    "step5/show_ip_route_bgp_active_srte.ref") + +# Memory leak test template +def test_memory_leak(): +    "Run the memory leak test and report results." +    tgen = get_topogen() +    if not tgen.is_memleak_enabled(): +        pytest.skip('Memory leak test/report is disabled') + +    tgen.report_memory_leaks() + +if __name__ == '__main__': +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index eaf7f90479..86f06b2af7 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -552,6 +552,7 @@ class TopoRouter(TopoGear):      RD_SHARP = 14      RD_BABEL = 15      RD_PBRD = 16 +    RD_PATH = 17      RD = {          RD_ZEBRA: "zebra",          RD_RIP: "ripd", @@ -569,6 +570,7 @@ class TopoRouter(TopoGear):          RD_SHARP: "sharpd",          RD_BABEL: "babeld",          RD_PBRD: "pbrd", +        RD_PATH: 'pathd',      }      def __init__(self, tgen, cls, name, **params): diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 20d60ebbef..d1e76866a7 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1096,6 +1096,7 @@ class Router(Node):              "sharpd": 0,              "babeld": 0,              "pbrd": 0, +            'pathd': 0          }          self.daemons_options = {"zebra": ""}          self.reportCores = True diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 3121551ee5..137a3d5a03 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -626,6 +626,22 @@ end                  ctx_keys = []                  current_context_lines = [] +            elif ( +                line == "exit" +                and len(ctx_keys) > 1 +                and ctx_keys[0].startswith("segment-routing") +            ): +                self.save_contexts(ctx_keys, current_context_lines) + +                # Start a new context +                ctx_keys = ctx_keys[:-1] +                current_context_lines = [] +                log.debug( +                    "LINE %-50s: popping segment routing sub-context to ctx%-50s", +                    line, +                    ctx_keys +                ) +              elif line in ["exit-address-family", "exit", "exit-vnc"]:                  # if this exit is for address-family ipv4 unicast, ignore the pop                  if main_ctx_key: @@ -637,7 +653,7 @@ end                      log.debug(                          "LINE %-50s: popping from subcontext to ctx%-50s",                          line, -                        ctx_keys, +                        ctx_keys                      )              elif line in ["exit-vni", "exit-ldp-if"]: @@ -727,6 +743,68 @@ end                  )                  ctx_keys.append(line) +            elif ( +                line.startswith("traffic-eng") +                and len(ctx_keys) == 1 +                and ctx_keys[0].startswith("segment-routing") +            ): + +                # Save old context first +                self.save_contexts(ctx_keys, current_context_lines) +                current_context_lines = [] +                log.debug( +                    "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line +                ) +                ctx_keys.append(line) + +            elif ( +                line.startswith("segment-list ") +                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 = [] +                log.debug( +                    "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line +                ) +                ctx_keys.append(line) + +            elif ( +                line.startswith("policy ") +                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 = [] +                log.debug( +                    "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line +                ) +                ctx_keys.append(line) + +            elif ( +                line.startswith("candidate-path ") +                and line.endswith(" dynamic") +                and len(ctx_keys) == 3 +                and ctx_keys[0].startswith("segment-routing") +                and ctx_keys[1].startswith("traffic-eng") +                and ctx_keys[2].startswith("policy") +            ): + +                # 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 candidate-path 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) @@ -1244,6 +1322,9 @@ def compare_context_objects(newconf, running):      # Compare the two Config objects to find the lines that we need to add/del      lines_to_add = []      lines_to_del = [] +    pollist_to_del = [] +    seglist_to_del = [] +    candidates_to_add = []      delete_bgpd = False      # Find contexts that are in newconf but not in running @@ -1326,6 +1407,32 @@ def compare_context_objects(newconf, running):                    (running_ctx_keys[:1], None) in lines_to_del):                  continue +            # Segment routing and traffic engineering never need to be deleted +            elif ( +                len(running_ctx_keys) > 1 +                and len(running_ctx_keys) < 3 +                and running_ctx_keys[0].startswith('segment-routing') +            ): +                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 ( +                len(running_ctx_keys) == 3 +                and running_ctx_keys[0].startswith('segment-routing') +                and running_ctx_keys[2].startswith('segment-list') +            ): +                seglist_to_del.append((running_ctx_keys, None)) + +            # Policies must be deleted after there candidate path, to be sure +            # we add them to a separate array that is going to be appended at the end +            elif ( +                len(running_ctx_keys) == 3 +                and running_ctx_keys[0].startswith('segment-routing') +                and running_ctx_keys[2].startswith('policy') +            ): +                pollist_to_del.append((running_ctx_keys, None)) +              # Non-global context              elif running_ctx_keys and not any(                  "address-family" in key for key in running_ctx_keys @@ -1340,6 +1447,14 @@ def compare_context_objects(newconf, running):                  for line in running_ctx.lines:                      lines_to_del.append((running_ctx_keys, line)) +    # if we have some policies commands to delete, append them to lines_to_del +    if len(pollist_to_del) > 0: +        lines_to_del.extend(pollist_to_del) + +    # if we have some segment list commands to delete, append them to lines_to_del +    if len(seglist_to_del) > 0: +        lines_to_del.extend(seglist_to_del) +      # Find the lines within each context to add      # Find the lines within each context to del      for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts): @@ -1349,7 +1464,19 @@ def compare_context_objects(newconf, running):              for line in newconf_ctx.lines:                  if line not in running_ctx.dlines: -                    lines_to_add.append((newconf_ctx_keys, line)) + +                    # candidate paths can only be added after the policy and segment list, +                    # so add them to a separate array that is going to be appended at the end +                    if ( +                        len(newconf_ctx_keys) == 3 +                        and newconf_ctx_keys[0].startswith('segment-routing') +                        and newconf_ctx_keys[2].startswith('policy ') +                        and line.startswith('candidate-path ') +                    ): +                        candidates_to_add.append((newconf_ctx_keys, line)) + +                    else: +                        lines_to_add.append((newconf_ctx_keys, line))              for line in running_ctx.lines:                  if line not in newconf_ctx.dlines: @@ -1358,10 +1485,27 @@ def compare_context_objects(newconf, running):      for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):          if newconf_ctx_keys not in running.contexts: -            lines_to_add.append((newconf_ctx_keys, None)) -            for line in newconf_ctx.lines: -                lines_to_add.append((newconf_ctx_keys, line)) +            # candidate paths can only be added after the policy and segment list, +            # so add them to a separate array that is going to be appended at the end +            if ( +                len(newconf_ctx_keys) == 4 +                and newconf_ctx_keys[0].startswith('segment-routing') +                and newconf_ctx_keys[3].startswith('candidate-path') +            ): +                candidates_to_add.append((newconf_ctx_keys, None)) +                for line in newconf_ctx.lines: +                    candidates_to_add.append((newconf_ctx_keys, line)) + +            else: +                lines_to_add.append((newconf_ctx_keys, None)) + +                for line in newconf_ctx.lines: +                    lines_to_add.append((newconf_ctx_keys, line)) + +    # if we have some candidate paths commands to add, append them to lines_to_add +    if len(candidates_to_add) > 0: +        lines_to_add.extend(candidates_to_add)      (lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del)      (lines_to_add, lines_to_del) = ignore_delete_re_add_lines( @@ -1523,10 +1667,11 @@ if __name__ == "__main__":          "staticd",          "vrrpd",          "ldpd", +        "pathd",      ]: -        log.error( -            "Daemon %s is not a valid option for 'show running-config'" % args.daemon -        ) +        msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon +        print(msg) +        log.error(msg)          sys.exit(1)      vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace) @@ -1574,6 +1719,8 @@ if __name__ == "__main__":          else:              running.load_from_show_running(args.daemon) + +          (lines_to_add, lines_to_del) = compare_context_objects(newconf, running)          lines_to_configure = [] diff --git a/tools/frr.in b/tools/frr.in index b860797d5b..889c075f81 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -27,7 +27,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter  # Local Daemon selection may be done by using /etc/frr/daemons.  # See /usr/share/doc/frr/README.Debian.gz for further information.  # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"  MAX_INSTANCES=5  RELOAD_SCRIPT="$D_PATH/frr-reload.py" diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c78132508f..93a63cd964 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -139,6 +139,7 @@ struct vtysh_client vtysh_client[] = {  	{.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL},  	{.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL},  	{.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL}, +	{.fd = -1, .name = "pathd", .flag = VTYSH_PATHD, .next = NULL},  };  /* Searches for client by name, returns index */ @@ -538,6 +539,11 @@ static int vtysh_execute_func(const char *line, int pager)  			    || saved_node == LDP_IPV6_IFACE_NODE)  			   && (tried == 1)) {  			vtysh_execute("exit"); +		} else if ((saved_node == SR_SEGMENT_LIST_NODE +			    || saved_node == SR_POLICY_NODE +			    || saved_node == SR_CANDIDATE_DYN_NODE) +			    && (tried > 0)) { +			vtysh_execute("exit");  		} else if (tried) {  			vtysh_execute("end");  			vtysh_execute("configure"); @@ -689,6 +695,7 @@ int vtysh_mark_file(const char *filename)  	int ret;  	vector vline;  	int tried = 0; +	bool ending;  	const struct cmd_element *cmd;  	int saved_ret, prev_node;  	int lineno = 0; @@ -740,6 +747,12 @@ int vtysh_mark_file(const char *filename)  				vty->node = LDP_L2VPN_NODE;  			}  			break; +		case SR_CANDIDATE_DYN_NODE: +			if (strncmp(vty_buf_copy, "  ", 2)) { +				vty_out(vty, " exit\n"); +				vty->node = SR_POLICY_NODE; +			} +			break;  		default:  			break;  		} @@ -812,6 +825,23 @@ int vtysh_mark_file(const char *filename)  			} else if ((prev_node == BFD_PEER_NODE)  				   && (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)) +				   && (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); +				if (ending) +					tried--; +				while (tried-- > 0) +					vty_out(vty, "exit\n"); +				if (ending) +					vty_out(vty, "end\n");  			} else if (tried) {  				vty_out(vty, "end\n");  			} @@ -1219,6 +1249,41 @@ static struct cmd_node pw_node = {  	.prompt = "%s(config-pw)# ",  }; +static struct cmd_node segment_routing_node = { +	.name = "segment-routing", +	.node = SEGMENT_ROUTING_NODE, +	.parent_node = CONFIG_NODE, +	.prompt = "%s(config-sr)# ", +}; + +static struct cmd_node sr_traffic_eng_node = { +	.name = "sr traffic-eng", +	.node = SR_TRAFFIC_ENG_NODE, +	.parent_node = SEGMENT_ROUTING_NODE, +	.prompt = "%s(config-sr-te)# ", +}; + +static struct cmd_node srte_segment_list_node = { +	.name = "srte segment-list", +	.node = SR_SEGMENT_LIST_NODE, +	.parent_node = SR_TRAFFIC_ENG_NODE, +	.prompt = "%s(config-sr-te-segment-list)# ", +}; + +static struct cmd_node srte_policy_node = { +	.name = "srte policy", +	.node = SR_POLICY_NODE, +	.parent_node = SR_TRAFFIC_ENG_NODE, +	.prompt = "%s(config-sr-te-policy)# ", +}; + +static struct cmd_node srte_candidate_dyn_node = { +	.name = "srte candidate-dyn", +	.node = SR_CANDIDATE_DYN_NODE, +	.parent_node = SR_POLICY_NODE, +	.prompt = "%s(config-sr-te-candidate)# ", +}; +  static struct cmd_node vrf_node = {  	.name = "vrf",  	.node = VRF_NODE, @@ -1974,6 +2039,60 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab  }  #endif /* HAVE_FABRICD */ +#if defined(HAVE_PATHD) +DEFUNSH(VTYSH_PATHD, segment_routing, segment_routing_cmd, +	"segment-routing", +	"Configure segment routing\n") +{ +	vty->node = SEGMENT_ROUTING_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, sr_traffic_eng, sr_traffic_eng_cmd, +	"traffic-eng", +	"Configure SR traffic engineering\n") +{ +	vty->node = SR_TRAFFIC_ENG_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, srte_segment_list, srte_segment_list_cmd, +	"segment-list WORD$name", +	"Segment List\n" +	"Segment List Name\n") +{ +	vty->node = SR_SEGMENT_LIST_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, srte_policy, srte_policy_cmd, +	"policy color (0-4294967295) endpoint <A.B.C.D|X:X::X:X>", +	"Segment Routing Policy\n" +	"SR Policy color\n" +	"SR Policy color value\n" +	"SR Policy endpoint\n" +	"SR Policy endpoint IPv4 address\n" +	"SR Policy endpoint IPv6 address\n") +{ +	vty->node = SR_POLICY_NODE; +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, srte_policy_candidate_dyn_path, +	srte_policy_candidate_dyn_path_cmd, +	"candidate-path preference (0-4294967295) name WORD dynamic", +	"Segment Routing Policy Candidate Path\n" +	"Segment Routing Policy Candidate Path Preference\n" +	"Administrative Preference\n" +	"Segment Routing Policy Candidate Path Name\n" +	"Symbolic Name\n" +	"Dynamic Path\n") +{ +	vty->node = SR_CANDIDATE_DYN_NODE; +	return CMD_SUCCESS; +} +#endif /* HAVE_PATHD */ +  DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd,  	"route-map WORD <deny|permit> (1-65535)",  	"Create route-map or enter route-map command mode\n" @@ -2347,6 +2466,18 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit",  	return vtysh_exit_keys(self, vty, argc, argv);  } +DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit", +	"Exit current mode and down to previous mode\n") +{ +	return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_PATHD, vtysh_quit_pathd, vtysh_quit_pathd_cmd, "quit", +	"Exit current mode and down to previous mode\n") +{ +	return vtysh_exit_pathd(self, vty, argc, argv); +} +  DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit",  	"Exit current mode and down to previous mode\n")  { @@ -4144,6 +4275,37 @@ void vtysh_init_vty(void)  	install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd);  #endif /* HAVE_BFDD */ +#if defined(HAVE_PATHD) +	install_node(&segment_routing_node); +	install_node(&sr_traffic_eng_node); +	install_node(&srte_segment_list_node); +	install_node(&srte_policy_node); +	install_node(&srte_candidate_dyn_node); + +	install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_pathd_cmd); +	install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_pathd_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &vtysh_exit_pathd_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &vtysh_quit_pathd_cmd); +	install_element(SR_SEGMENT_LIST_NODE, &vtysh_exit_pathd_cmd); +	install_element(SR_SEGMENT_LIST_NODE, &vtysh_quit_pathd_cmd); +	install_element(SR_POLICY_NODE, &vtysh_exit_pathd_cmd); +	install_element(SR_POLICY_NODE, &vtysh_quit_pathd_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, &vtysh_exit_pathd_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, &vtysh_quit_pathd_cmd); + +	install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd); +	install_element(SR_TRAFFIC_ENG_NODE, &vtysh_end_all_cmd); +	install_element(SR_SEGMENT_LIST_NODE, &vtysh_end_all_cmd); +	install_element(SR_POLICY_NODE, &vtysh_end_all_cmd); +	install_element(SR_CANDIDATE_DYN_NODE, &vtysh_end_all_cmd); + +	install_element(CONFIG_NODE, &segment_routing_cmd); +	install_element(SEGMENT_ROUTING_NODE, &sr_traffic_eng_cmd); +	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); +#endif /* HAVE_PATHD */ +  	/* keychain */  	install_node(&keychain_node);  	install_element(CONFIG_NODE, &key_chain_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index d2675a81b9..9683518b67 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -43,6 +43,7 @@ DECLARE_MGROUP(MVTYSH)  #define VTYSH_BFDD      0x10000  #define VTYSH_FABRICD   0x20000  #define VTYSH_VRRPD     0x40000 +#define VTYSH_PATHD     0x80000  #define VTYSH_WAS_ACTIVE (-2) @@ -51,7 +52,7 @@ DECLARE_MGROUP(MVTYSH)  /* watchfrr is not in ALL since library CLI functions should not be   * run on it (logging & co. should stay in a fixed/frozen config, and   * things like prefix lists are not even initialised) */ -#define VTYSH_ALL	  VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD +#define VTYSH_ALL	  VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD|VTYSH_PATHD  #define VTYSH_ACL         VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA  #define VTYSH_RMAP	  VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD  #define VTYSH_INTERFACE	  VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD diff --git a/yang/frr-pathd.yang b/yang/frr-pathd.yang new file mode 100644 index 0000000000..03f0d3b024 --- /dev/null +++ b/yang/frr-pathd.yang @@ -0,0 +1,480 @@ +module frr-pathd { +  yang-version 1.1; +  namespace "http://frrouting.org/yang/pathd"; +  prefix frr-pathd; + +  import ietf-inet-types { +    prefix inet; +  } +  import ietf-yang-types { +    prefix yang; +  } +  import ietf-routing-types { +    prefix rt-types; +  } +  import frr-interface { +    prefix frr-interface; +  } + +  organization +    "Free Range Routing"; +  contact +    "FRR Users List:       <mailto:frog@lists.frrouting.org> +     FRR Development List: <mailto:dev@lists.frrouting.org>"; +  description +    "This module defines a model for managing FRR pathd daemon."; + +  revision 2018-11-06 { +    description +      "Initial revision."; +  } + +  typedef protocol-origin-type { +    description +      "Indication for the protocol origin of an object."; +    type enumeration { +      enum pcep { +        value 1; +        description "The object was created through PCEP"; +      } +      enum bgp { +        value 2; +        description "The object was created through GBP"; +      } +      enum local { +        value 3; +        description "The object was created through CLI, Yang model via Netconf, gRPC, etc"; +      } +    } +  } + +  typedef originator-type { +        type string { +          length "1..64"; +        } +        description +          "Identifier of the originator of an object, could be 'config', '1.1.1.1:4189' or '2001:db8:85a3::8a2e:370:7334:4189'"; +      } + +  container pathd { +    container srte { +      list segment-list { +        key "name"; +        description "Segment-list properties"; +        leaf name { +          type string { +            length "1..64"; +          } +          description "Segment-list name"; +        } +        leaf protocol-origin { +          type protocol-origin-type; +          mandatory true; +          description +            "Indication for the protocol origin of the segment list."; +        } +        leaf originator { +          type originator-type; +          mandatory true; +          description "Originator of the segment list"; +        } +        list segment { +          key "index"; +          description "Configure Segment/hop at the index"; +          leaf index { +            type uint32; +            description "Segment index"; +          } +          leaf sid-value { +            type rt-types:mpls-label; +            mandatory true; +            description "MPLS label value"; +          } +          container nai { +            presence "The segement has a Node or Adjacency Identifier"; +            leaf type { +              description "NAI type"; +              mandatory true; +              type enumeration { +                enum ipv4_node { +                  value 1; +                  description "IPv4 node identifier"; +                } +                enum ipv6_node { +                  value 2; +                  description "IPv6 node identifier"; +                } +                enum ipv4_adjacency { +                  value 3; +                  description "IPv4 adjacency"; +                } +                enum ipv6_adjacency { +                  value 4; +                  description "IPv6 adjacency"; +                } +                enum ipv4_unnumbered_adjacency { +                  value 5; +                  description "IPv4 unnumbered adjacency"; +                } +              } +            } +            leaf local-address { +              type inet:ip-address; +              mandatory true; +            } +            leaf local-interface { +              type uint32; +              mandatory true; +              when "../type = 'ipv4_unnumbered_adjacency'"; +            } +            leaf remote-address { +              type inet:ip-address; +              mandatory true; +              when "../type = 'ipv4_adjacency' or ../type = 'ipv6_adjacency' or ../type = 'ipv4_unnumbered_adjacency'"; +            } +            leaf remote-interface { +              type uint32; +              mandatory true; +              when "../type = 'ipv4_unnumbered_adjacency'"; +            } +          } +        } +      } +      list policy { +        key "color endpoint"; +        unique "name"; +        leaf color { +          type uint32; +          description +            "Color of the SR Policy."; +        } +        leaf endpoint { +          type inet:ip-address; +          description +            "Indication for the endpoint of the SR Policy."; +        } +        leaf name { +          type string { +            length "1..64"; +          } +          description +            "Name of the SR Policy."; +        } +        leaf binding-sid { +          type rt-types:mpls-label; +          description +            "BSID of the SR Policy."; +        } +        leaf is-operational { +          type boolean; +          config false; +          description +            "True if a valid candidate path of this policy is operational in zebra, False otherwise"; +        } +        list candidate-path { +          unique "name"; +          description +            "List of Candidate Paths of the SR Policy."; +          key "preference"; +          leaf preference { +            type uint32; +            description +              "Administrative preference."; +          } +          leaf name { +            type string { +              length "1..64"; +            } +            mandatory true; +            description +              "Symbolic Name of the Candidate Path."; +          } +          leaf is-best-candidate-path { +            type boolean; +            config false; +            description +              "True if the candidate path is the best candidate path, False otherwise"; +          } +          leaf protocol-origin { +            type protocol-origin-type; +            mandatory true; +            description +              "Indication for the protocol origin of the Candidate Path."; +          } +          leaf originator { +            type originator-type; +            mandatory true; +            description "Originator of the candidate path"; +          } +          leaf discriminator { +            type uint32; +            config false; +            description "Candidate path distinguisher"; +          } +          leaf type { +            description +              "Type of the Candidate Path."; +            mandatory true; +            type enumeration { +              enum explicit { +                value 1; +              } +              enum dynamic { +                value 2; +              } +            } +          } +          leaf segment-list-name { +            type leafref { +              path ../../../segment-list/name; +            } +            description +              "The name of the Segment List to use as LSP."; +          } +          container constraints { +            when "../type = 'dynamic'"; +            description +              "Generic dynamic path constraints"; +            container bandwidth { +              presence "If the candidate has a bandwidth constraint"; +              description +                "The bandwidth needed by the candidate path."; +              leaf required { +                type boolean; +                default "true"; +                description +                  "If the bandwidth limitation is a requirement or only a suggestion"; +              } +              leaf value { +                mandatory true; +                type decimal64 { +                    fraction-digits 6; +                } +              } +            } +            container affinity { +              description +                "Affinity let you configure how the links should be used when calculating a path."; +              leaf exclude-any { +                type uint32; +                description +                  "A 32-bit vector representing a set of attribute filters which renders a link unacceptable."; +              } +              leaf include-any { +                type uint32; +                description +                  "A 32-bit vector representing a set of attribute filters which renders a link acceptable. A null set (all bits set to zero) automatically passes."; +              } +              leaf include-all { +                type uint32; +                description +                  "A 32-bit vector representing a set of attribute filters which must be present for a link to be acceptable.  A null set (all bits set to zero) automatically passes."; +              } +            } +            list metrics { +              key "type"; +              leaf type { +                description +                  "Type of the metric."; +                type enumeration { +                  enum igp { +                    value 1; +                    description "IGP metric"; +                  } +                  enum te { +                    value 2; +                    description "TE metric"; +                  } +                  enum hc { +                    value 3; +                    description "Hop Counts"; +                  } +                  enum abc { +                    value 4; +                    description "Aggregate bandwidth consumption"; +                  } +                  enum lmll { +                    value 5; +                    description "Load of the most loaded link"; +                  } +                  enum cigp { +                    value 6; +                    description "Cumulative IGP cost"; +                  } +                  enum cte { +                    value 7; +                    description "Cumulative TE cost"; +                  } +                  enum pigp { +                    value 8; +                    description "P2MP IGP metric"; +                  } +                  enum pte { +                    value 9; +                    description "P2MP TE metric"; +                  } +                  enum phc { +                    value 10; +                    description "P2MP hop count metric"; +                  } +                  enum msd { +                    value 11; +                    description "Segment-ID (SID) Depth"; +                  } +                  enum pd { +                    value 12; +                    description "Path Delay metric"; +                  } +                  enum pdv { +                    value 13; +                    description "Path Delay Variation metric"; +                  } +                  enum pl { +                    value 14; +                    description "Path Loss metric"; +                  } +                  enum ppd { +                    value 15; +                    description "P2MP Path Delay metric"; +                  } +                  enum ppdv { +                    value 16; +                    description "P2MP Path Delay variation metric"; +                  } +                  enum ppl { +                    value 17; +                    description "P2MP Path Loss metric"; +                  } +                  enum nap { +                    value 18; +                    description "Number of adaptations on a path"; +                  } +                  enum nlp { +                    value 19; +                    description "Number of layers on a path"; +                  } +                  enum dc { +                    value 20; +                    description "Domain Count metric"; +                  } +                  enum bnc { +                    value 21; +                    description "Border Node Count metric"; +                  } +                } +              } +              leaf required { +                type boolean; +                default "true"; +                description +                  "If the metric is a requirement, or if it is only a suggestion"; +              } +              leaf is-bound { +                type boolean; +                description +                  "Defines if the value is a bound (a maximum) for the path metric that must not be exceeded."; +              } +              leaf is-computed { +                type boolean; +                description +                  "Defines if the value has been generated by the originator of the path."; +              } +              leaf value { +                mandatory true; +                type decimal64 { +                    fraction-digits 6; +                } +              } +            } +            container objective-function { +              presence "If the candidate has an objective function constraint"; +              description +                "Define objective function constraint as a list of prefered functions"; +              leaf required { +                type boolean; +                default "true"; +                description +                  "If an objective function is a requirement, or if it is only a suggestion"; +              } +              leaf type { +                description +                  "Type of objective function."; +                mandatory true; +                type enumeration { +                  enum mcp { +                    value 1; +                    description "Minimum Cost Path"; +                  } +                  enum mlp { +                    value 2; +                    description "Minimum Load Path"; +                  } +                  enum mbp { +                    value 3; +                    description "Maximum residual Bandwidth Path"; +                  } +                  enum mbc { +                    value 4; +                    description "Minimize aggregate Bandwidth Consumption"; +                  } +                  enum mll { +                    value 5; +                    description "Minimize the Load of the most loaded Link"; +                  } +                  enum mcc { +                    value 6; +                    description "Minimize the Cumulative Cost of a set of paths"; +                  } +                  enum spt { +                    value 7; +                    description "Shortest Path Tree"; +                  } +                  enum mct { +                    value 8; +                    description "Minimum Cost Tree"; +                  } +                  enum mplp { +                    value 9; +                    description "Minimum Packet Loss Path"; +                  } +                  enum mup { +                    value 10; +                    description "Maximum Under-Utilized Path"; +                  } +                  enum mrup { +                    value 11; +                    description "Maximum Reserved Under-Utilized Path"; +                  } +                  enum mtd { +                    value 12; +                    description "Minimize the number of Transit Domains"; +                  } +                  enum mbn { +                    value 13; +                    description "Minimize the number of Border Nodes"; +                  } +                  enum mctd { +                    value 14; +                    description "Minimize the number of Common Transit Domains"; +                  } +                  enum msl { +                    value 15; +                    description "Minimize the number of Shared Links"; +                  } +                  enum mss { +                    value 16; +                    description "Minimize the number of Shared SRLGs"; +                  } +                  enum msn { +                    value 17; +                    description "Minimize the number of Shared Nodes"; +                  } +                } +              } +            } +          } +        } +      } +    } +  } +} diff --git a/yang/subdir.am b/yang/subdir.am index 5be93dc4e9..47fc508901 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -82,3 +82,7 @@ dist_yangmodels_DATA += yang/frr-bgp-bmp.yang  dist_yangmodels_DATA += yang/frr-bgp-types.yang  dist_yangmodels_DATA += yang/frr-bgp.yang  endif + +if PATHD +dist_yangmodels_DATA += yang/frr-pathd.yang +endif  | 
