From 701ac838a3a16eb86950a2817f50018299823439 Mon Sep 17 00:00:00 2001 From: Sucheta Mahara Date: Thu, 2 Apr 2020 13:46:23 -0700 Subject: [PATCH 1/5] Staic route configuration from management framework. --- doc/SONiC_static_route_hdl.md | 370 ++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 doc/SONiC_static_route_hdl.md diff --git a/doc/SONiC_static_route_hdl.md b/doc/SONiC_static_route_hdl.md new file mode 100644 index 00000000000..d0e490b7c4a --- /dev/null +++ b/doc/SONiC_static_route_hdl.md @@ -0,0 +1,370 @@ +# Static IP route Configuration + +Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management framework. + +# High Level Design Document +#### Rev 0.1 + +# Table of Contents + * [List of Tables](#list-of-tables) + * [Revision](#revision) + * [About This Manual](#about-this-manual) + * [Scope](#scope) + * [Definition/Abbreviation](#definitionabbreviation) + +# List of Tables +[Table 1: Abbreviations](#table-1-abbreviations) + +# Revision +| Rev | Date | Author | Change Description | +|:---:|:-----------:|:---------------------------:|-----------------------------------| +| 0.1 | 03/18/2020 | Sucheta Mahara | initial draft | +| 0.2| 03/20/20202 | Venkatesan Mahalinga | draft | +| 0.2 | 03/23/2020 | Zhenhong Zhao | FRR Config Support | + +# About this Manual +This document provides general information about configuring static routes via Management CLI/REST/gNMI in Sonic. +# Scope +Covers general design for supporting static routes in SONiC Management framework.. + +# Definition/Abbreviation + +### Table 1: Abbreviations +| **Term** | **Meaning** | +|--------------------------|-------------------------------------| +| IP | Internet Protocol | +| VRF | Virtual routing and forwarding + + +# 1 Feature Overview + +The documents covers the various interfaces for configuring static routes using SONiC management framework. + +## 1.1 Requirements + +### 1.1.1 Functional Requirements + +Provide ability to configure IPv4 and IPv6 static routes using SONiC management framework. + +### 1.1.2 Configuration and Management Requirements + - Implement Static ip route CLI Commands. + - REST set/get support for Static IPv4 and IPv6 routes. + - gNMI set/get support for Static IPv4 and IPv6 routes. + +### 1.1.3 Scalability Requirements +### 1.1.4 Warm Boot Requirements + +## 1.2 Design Overview +### 1.2.1 Basic Approach +### 1.2.2 Container +### 1.2.3 SAI Overview +# 2 Functionality +## 2.1 Target Deployment Use Cases +## 2.2 Functional Description +# 3 Design +## 3.1 Overview +An existing table STATIC_ROUTE (which is not used currently) will be used to write static route from the transformer for any CLI, rest or gNMI request. This table will be monitored by bgpcfgd daemon and the config will be sent to vtysh shell for configuring in FRR. +## 3.2 DB Changes +### 3.2.1 CONFIG DB +STATIC_ROUTE table in config DB will be used to support this feature. + +#### 3.2.1.1 STATIC_ROUTE +```JSON +;Defines IP static route table +; +;Status: stable + +key = STATIC_ROUTE|vrf-name|prefix ; +vrf-name = 1\*15VCHAR ; VRF name +prefix = IPv4Prefix / IPv6prefix +nexthop = IPv4Address / IPv6Address; List of gateway addresses; +ifname = List of interfaces +distance = {0..255};List of distances. + Its a Metric used to specify preference of next-hop + if this distance is not set 0 will be set to maintain the set; +nexthop-vrf = 1\*15VCHAR ; list of next-hop VRFs. It is set only if interface is not + in the current VRF . The value is set to VRF name + to which the interface belongs. +``` +The nexthop-vrf if set, will allow to create a leaked route in the current VRF(vrf-name). The vrf-name, nexthop-vrf, prefix, ifname and distance are all parameters required to configure static routes and route leaks in vtysh. +If an interface is moved from one VRF to another and it exist in the STATIC_ROUTE table, error will be returned by the backend. User is expected to remove all static routes pertaining to the interface before binding the interface to a different VRF. +If prefix is IPv4Prefix nexthop will have IPv4Address and if prefix is IPv6Prefix nexthop will have IPv6Address. +In this table each entry based on the index in the lists of nexthop, ifname, distance and nexthop-vrf is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. + +In the example table below (0.0.0.0, Ethernet0, 10, default) , (2.2.2.2, "", 0,"") and (3.3.3.3, Ethernet12, 30, Vrf-GREEN) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. + +```JSON +key = STATIC_ROUTE|Vrf-RED|10.11.1.0/24; +vrf-name = Vrf-RED +prefix = 10.11.1.0/24 +nexthop = 0.0.0.0, 2.2.2.1, 3.3.3.3 +ifname = "Ethernet0", "", "Ethernet12" +distance = 10,0,30 +nexthop-vrf = "default", "" , "Vrf-GREEN" +``` + +Note: This model is proposed in line with ROUTE_TABLE model in application DB used for route updates and "vrf hld" description of STATIC_ROUTE table (which is currently not in use). Some default values like 0.0.0.0 and empty strings are used to complete a set. See the CLI example later to see how the table is filled up step-by-step. + + +### 3.2.2 APP DB +### 3.2.3 STATE DB +### 3.2.4 ASIC DB +### 3.2.5 COUNTER DB + +## 3.3 Switch State Service Design +### 3.3.1 Orchestration Agent +### 3.3.2 Other Process +## 3.4 SyncD +## 3.5 SAI +## 3.6 User Interface +### 3.6.1 Data Models + +The following open config YANG model will be used to implement this feature. + + 1) openconfig-network-instance.yang + 2) openconfig-local-routing.yang +openconfig-local-routing-ext.yang will be used to specify unsupported fields and deviations. + +Supported YANG containers:- +```diff +module: openconfig-network-instance + +--rw network-instances + +--rw network-instance* [name] + +--rw protocols + +--rw protocol* [identifier name] + +--rw identifier -> ../config/identifier + +--rw name -> ../config/name + +--rw static-routes + +--rw static* [prefix] + +--rw prefix -> ../config/prefix + +--rw config + | +--rw prefix? inet:ip-prefix + +--ro state + | +--ro prefix? inet:ip-prefix + +--rw next-hops + +--rw next-hop* [index] + +--rw index -> ../config/index ----(Read note below) + +--rw config + | +--rw index? string + | +--rw next-hop? union + | +--rw metric? uint32 + +--ro state + | +--ro index? string + | +--ro next-hop? union + | +--ro metric? uint32 + +--rw interface-ref + | +--rw config + | | +--rw interface? -> /oc-if:interfaces/interface/name + | +--ro state + | +--ro interface? -> /oc-if:interfaces/interface/name + + + +``` + + +Note: Each next-hop entry has a key called "index" in openconfig yang as shown above. This key is used to uniquely identify a entry in the list of next-hops. This key will be formed based on "interface (interface name if present) + next-hop (next-hop-ip if present) + vrf-name" and is expected to be provided correctly by the user. Either interface name or next-hop-ip or both should be present for any route to be accessed. i.e Ethernet0_2.2.2.2_default, 3.3.3.3_Vrf-RED. This key will not be stored in config DB. + + +A new sonic-static-route.yang is defined to store entries in STATIC_ROUTE table in configDB. + +```diff + +module: sonic-static-route + +--rw sonic-static-route + +--rw STATIC_ROUTE + +--rw STATIC_ROUTE_LIST* [vrf-name prefix] + +--rw vrf-name union + +--rw prefix inet:ip-prefix + +--rw nexthop* inet:ip-address + +--rw ifname* union + +--rw distance* uint32 + +--rw nexthop-vrf* string + + +``` + +### 3.6.2 CLI +#### 3.6.2.1 Configuration Commands +#### 3.6.2.2 ip/ipv6 route config command +ip route command is used to configure IPv4 static routes in SONiC. +ipv6 route command is used to configure IPv6 static routes in SONiC. +##### 3.6.2.2.1 +Syntax + +Vrf and distance metric are optional in the CLI. + +``` +ip route vrf {[interface ] | [] | [ interface ]} +``` + +``` +ipv6 route vrf {[interface ] | [] | [ interface ]} + +``` + + +Syntax Description: + +| Keyword | Description | +|:-----------------:|:-----------:| +| vrf | The static routes will be configured in the specified VRF. If not specified, default VRF will be used. +| prefix | This is the destination prefix for IPv4 or IPv6 routes. +| interface | Specifies the interface to be used for this route. If the interface doesn't belong to the current VRF, it is considered a route-leak. The VRF to which this interface belongs will be set as the nexthop-vrf in the STATIC_ROUTE table . +| next-hop-ip | This provides the gateway IP for the prefix. +| distance Metric| Specifies distance value for this route. Value form 1 to 255. + + +Example: + +#### IPv4 examples +```` +sonic(config)# ip route 10.1.1.1/24 10.1.1.3 + +In configDB new STATIC_ROUTE table will be filled with following entries for default VRF and prefix:- +key = STATIC_ROUTE|default|10.1.1.1/24; +vrf-name = default +prefix = 10.1.1.1/24 +nexthop = 10.1.1.3 +ifname = "" +distance = 0 +nexthop-vrf = "" + + +sonic(config)# ip route 10.1.1.1/24 Ethernet12 20 + +Assumption is Ethernet12 is configured in Vrf-RED by user. +STATIC_ROUTE table entries are updated for newly added route:- +key = STATIC_ROUTE|default|10.1.1.1/24; +vrf-name = default +prefix = 10.1.1.1/24 +nexthop = 10.1.1.3, 0.0.0.0 +ifname = "", Ethernet12 +distance = 0, 20 +nexthop-vrf = "", Vrf-RED. +```` + +```` + +sonic(config)# ip route vrf Vrf-RED 10.5.6.6/24 Ethernet0 10 + +Assumption is Ethernet0 is in default VRF. +A new STATIC_ROUTE table will be filled with following entries for Vrf-RED and prefix:- +key = STATIC_ROUTE|Vrf-RED|10.5.6.6/24; +vrf-name = Vrf-RED +prefix = 10.5.6.6/24 +nexthop = 0.0.0.0 +ifname = Ethernet0 +distance = 10 +nexthop-vrf = default + + +```` +#### IPv6 examples +```` +sonic(config)# ipv6 route 2::/64 Ethernet16 + +sonic(config)# ipv6 route 2001:FF21:1:1::/64 18:2:1::1 100 +```` +##### 3.6.2.2.2 ipv4 /ipv6 Command to delete a next-hop. + +The vrf is an optional parameter. + +``` +no ip route vrf {[interface ] | [] | [ interface ]} + +``` + +``` +no ipv6 route vrf {[interface ] | [] | [ interface ]} +``` +#### 3.6.2.3 Debug Commands +#### 3.6.2.4 IS-CLI Compliance +The following table maps SONiC CLI commands to corresponding IS-CLI commands. The compliance column identifies how the command comply to the IS-CLI syntax: + +- **IS-CLI drop-in replace** \u2013 meaning that it follows exactly the format of a pre-existing IS-CLI command. +- **IS-CLI-like** \u2013 meaning that the exact format of the IS-CLI command could not be followed, but the command is similar to other commands for IS-CLI (e.g. IS-CLI may not offer the exact option, but the command can be positioned is a similar manner as others for the related feature). +- **SONiC** - meaning that no IS-CLI-like command could be found, so the command is derived specifically for SONiC. + +|CLI Command|Compliance|IS-CLI Command (if applicable)| Link to the web site identifying the IS-CLI command (if applicable)| +|:---:|:-----------:|:------------------:|-----------------------------------| +|ip route |IS-CLI drop-in replace |Similar to OS-10 | | +|ipv6 route| IS-CLI drop-in replace | Similar to OS-10 | | +|no ip route |IS-CLI drop-in replace |Similar to OS-10 | | +|no ipv6 route |IS-CLI drop-in replace |Similar to OS-10 | | + + +### 3.6.3 REST API Support +#### 3.6.3.1 +##### Get all support for both ARPs and Neighbors +Various REST operations (POST/PUT/PATCH/GET/DELETE) will be supported for the static route configuration. + +## 3.7 FRR Configuration Support +### 3.7.1 Configuration Mapping to FRR +Bgpcfgd daemon will be used to forward configurations stored in STATIC_ROUTE table to FRR staticd daemon. It will subscribe to listen to STATIC_ROUTE table and if there is data update, it will convert associated data to FRR vtysh command request and send to FRR daemon to configure static route on Linux kernel. +#### 3.7.1.1 Table entry to command mapping +FRR vtysh command is composed with VRF/IP_prefix and nexthop data fields in STATIC_ROUTE table entry +#### FRR command syntax +``` +configure terminal +vrf +[no ]ip route [] [] [] [nexthop-vrf ] +``` +#### Mapping from table entry to FRR command arguments +|Parameter Name|Table Entry Key or Field|Type|Default or Null Value| +|:---:|:-----------:|:------------------:|-----------------------------------| +|src_vrf|first entry key*|optional|"default"| +|ip_prefix|second entry key*|mandatory|-| +|nexthop_ip|entry field **nexthop****|optional |"0.0.0.0" or "::"***| +|interface|entry field **ifname****|optional |""| +|distance|entry field **distance****|optional|"0"| +|nexthop_vrf|entry field **nexthop-vrf****|optional|""| + +Note: +- *If table entry key contains only one item, it should be prefix_ip and src_vrf will be used with default value "default". +- **Each argument uses one item of corresponding list in table entry. If the item in list is "Null" value, this optional argument will not be added to mapped FRR commnand. +- ***The "Null" value of nexthop_ip should be chosen based on the address family of ip_prefix + +#### Example of table entry and correspoinding FRR runnning config +Data in STATIC_ROUTE table: +``` +127.0.0.1:6379[4]> hgetall STATIC_ROUTE|Vrf-test|1.1.1.0/16 +1) "nexthop@" +2) "2.2.2.2,0.0.0.0" +3) "distance@" +4) "10,20" +5) "ifname@" +6) "Ethernet0,Ethernet4" +7) "nexthop-vrf@" +8) "Vrf-BLUE,Vrf-RED" +``` +FRR running config: +``` +! +vrf Vrf-test + ip route 1.1.0.0/16 2.2.2.2 Ethernet0 10 nexthop-vrf Vrf-BLUE + ip route 1.1.0.0/16 Ethernet4 20 nexthop-vrf Vrf-RED + exit-vrf +! +``` +### 3.7.2 Configuration Reload +All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon started. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is be loaded by FRR daemon to configure static routes to system. + +# 4 Flow Diagrams + +# 5 Error Handling + +# 6 Serviceability and Debug +# 7 Warm Boot Support + +# 8 Scalability + +# 9 Unit Test and automation +The following test cases will be tested using CLI/REST/gNMI management interfaces. +#### Static route Unit test cases: +1) Verify creation deletion of multiple next-hops for a prefix list in default vrf. +2) Verify creation deletion of multiple next-hops for a prefix list in not-default vrf. +3) Verify creation deletion of multiple next-hops for a prefix list for route leak case where the specified interface belongs to a different VRF. +4) Verify the get/set operation passes for a particular next-hop when supplying the correct index as key. +#### Automation +Spytest cases will be incremented for new CLI and APIs. From e9533258e7cd806760bdf75ab79c116e6997e030 Mon Sep 17 00:00:00 2001 From: Sucheta Mahara Date: Thu, 2 Apr 2020 15:59:52 -0700 Subject: [PATCH 2/5] Typo fixed --- doc/SONiC_static_route_hdl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/SONiC_static_route_hdl.md b/doc/SONiC_static_route_hdl.md index d0e490b7c4a..2ca2980a46a 100644 --- a/doc/SONiC_static_route_hdl.md +++ b/doc/SONiC_static_route_hdl.md @@ -348,7 +348,7 @@ vrf Vrf-test ! ``` ### 3.7.2 Configuration Reload -All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon started. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is be loaded by FRR daemon to configure static routes to system. +All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon starts. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is be loaded by FRR daemon to configure static routes to system. # 4 Flow Diagrams From 651f69b74e0864e9ca891245e29eafd2b4370be8 Mon Sep 17 00:00:00 2001 From: Sucheta Mahara Date: Fri, 10 Apr 2020 14:19:12 -0700 Subject: [PATCH 3/5] Ability to set nexthop-vrf explicitly and blackhole support is added. --- doc/SONiC_static_route_hdl.md | 132 +++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 40 deletions(-) diff --git a/doc/SONiC_static_route_hdl.md b/doc/SONiC_static_route_hdl.md index 2ca2980a46a..dabbd48fe5b 100644 --- a/doc/SONiC_static_route_hdl.md +++ b/doc/SONiC_static_route_hdl.md @@ -1,6 +1,6 @@ # Static IP route Configuration -Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management framework. +Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management framework for non-management routes. # High Level Design Document #### Rev 0.1 @@ -25,7 +25,7 @@ Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management # About this Manual This document provides general information about configuring static routes via Management CLI/REST/gNMI in Sonic. # Scope -Covers general design for supporting static routes in SONiC Management framework.. +Covers general design for supporting static routes in SONiC Management framework. # Definition/Abbreviation @@ -81,28 +81,46 @@ nexthop = IPv4Address / IPv6Address; List of gateway addresses; ifname = List of interfaces distance = {0..255};List of distances. Its a Metric used to specify preference of next-hop - if this distance is not set 0 will be set to maintain the set; -nexthop-vrf = 1\*15VCHAR ; list of next-hop VRFs. It is set only if interface is not + if this distance is not set, 0 will be set to maintain the set; +nexthop-vrf = 1\*15VCHAR ; list of next-hop VRFs. It should be set only if ifname or nexthop IP is not in the current VRF . The value is set to VRF name - to which the interface belongs. + to which the interface or nexthop IP belongs for route leaks. +blackhole = List of boolean; true if the next-hop route is blackholed. ``` -The nexthop-vrf if set, will allow to create a leaked route in the current VRF(vrf-name). The vrf-name, nexthop-vrf, prefix, ifname and distance are all parameters required to configure static routes and route leaks in vtysh. +The nexthop-vrf if set, will allow to create a leaked route in the current VRF(vrf-name). The vrf-name, nexthop-vrf, prefix, ifname, distance and blackhole are all parameters required to configure static routes and route leaks in vtysh. If an interface is moved from one VRF to another and it exist in the STATIC_ROUTE table, error will be returned by the backend. User is expected to remove all static routes pertaining to the interface before binding the interface to a different VRF. If prefix is IPv4Prefix nexthop will have IPv4Address and if prefix is IPv6Prefix nexthop will have IPv6Address. -In this table each entry based on the index in the lists of nexthop, ifname, distance and nexthop-vrf is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. +In this table each entry based on the index in the lists of nexthop, ifname, distance, nexthop-vrf and blackhole is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. -In the example table below (0.0.0.0, Ethernet0, 10, default) , (2.2.2.2, "", 0,"") and (3.3.3.3, Ethernet12, 30, Vrf-GREEN) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. +In the example -1 table below (0.0.0.0, Ethernet0, 10, default, false) , (2.2.2.2, "", 0,"", false), (0.0.0.0, Ethernet12, 30, Vrf-GREEN, false) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. +In example -2 the blackhole is true for second index and only distance is relevant in blackholed route. Due to blackhole entry (with metric 40) all packets matching 10.12.11.0/24 will be discarded if other specified nexthop is inactive. ```JSON +Example -1 :-- + key = STATIC_ROUTE|Vrf-RED|10.11.1.0/24; vrf-name = Vrf-RED prefix = 10.11.1.0/24 -nexthop = 0.0.0.0, 2.2.2.1, 3.3.3.3 -ifname = "Ethernet0", "", "Ethernet12" +nexthop = 0.0.0.0, 2.2.2.1, 0.0.0.0 +ifname = "Ethernet0", "", "Ethernet12" distance = 10,0,30 nexthop-vrf = "default", "" , "Vrf-GREEN" +blackhole = false, false, false + +EXAMPLE 2:- +key = STATIC_ROUTE|default|10.12.11.0/24; +vrf-name = default +prefix = 10.12.11.0/24 +nexthop = 2.2.2.3, "" +ifname = "", "" +distance = 10,40 +nexthop-vrf = "","" +blackhole = false, true + + ``` + Note: This model is proposed in line with ROUTE_TABLE model in application DB used for route updates and "vrf hld" description of STATIC_ROUTE table (which is currently not in use). Some default values like 0.0.0.0 and empty strings are used to complete a set. See the CLI example later to see how the table is filled up step-by-step. @@ -123,7 +141,7 @@ The following open config YANG model will be used to implement this feature. 1) openconfig-network-instance.yang 2) openconfig-local-routing.yang -openconfig-local-routing-ext.yang will be used to specify unsupported fields and deviations. +openconfig-local-routing-ext.yang will be used to specify unsupported fields and extensions. Supported YANG containers:- ```diff @@ -148,10 +166,14 @@ module: openconfig-network-instance | +--rw index? string | +--rw next-hop? union | +--rw metric? uint32 + | +--rw oc-local-routing-ext:nexthop-vrf-name? string + | +--rw oc-local-routing-ext:blackhole? boolean +--ro state | +--ro index? string | +--ro next-hop? union | +--ro metric? uint32 + | +--rw oc-local-routing-ext:nexthop-vrf-name? string + | +--rw oc-local-routing-ext:blackhole? boolean +--rw interface-ref | +--rw config | | +--rw interface? -> /oc-if:interfaces/interface/name @@ -161,9 +183,14 @@ module: openconfig-network-instance ``` +New Parameters +| Keyword | Description | +|:-----------------:|:-----------:| +oc-local-routing-ext:nexthop-vrf-name|Specify the nexthop-vrf incase interface or next-hop is not in current instance(i.e VRF). This parameter is explicitly required by vtysh to correctly install route leaks. +oc-local-routing-ext:blackhole | If set to true the nexhop is a blackholed route. Only metric is relevant when configuring blackhole next-hop for a prefix. -Note: Each next-hop entry has a key called "index" in openconfig yang as shown above. This key is used to uniquely identify a entry in the list of next-hops. This key will be formed based on "interface (interface name if present) + next-hop (next-hop-ip if present) + vrf-name" and is expected to be provided correctly by the user. Either interface name or next-hop-ip or both should be present for any route to be accessed. i.e Ethernet0_2.2.2.2_default, 3.3.3.3_Vrf-RED. This key will not be stored in config DB. +Note: Each next-hop entry has a key called "index" in openconfig yang as shown above. This key is used to uniquely identify a entry in the list of next-hops. This key will be formed based on "interface (interface name if present) + next-hop (next-hop-ip if present) + vrf-name" and is expected to be provided correctly by the user for non-blackhole routes. For blackhole route DROP + vrf-name will be used as key. Either interface name or next-hop-ip or both or blackhole should be present for any route to be accessed. i.e Ethernet0_2.2.2.2_default, 3.3.3.3_Vrf-RED, DROP_Vrf-GREEN. This key will not be stored in config DB. For default instance vrf-name in the key is optional. A new sonic-static-route.yang is defined to store entries in STATIC_ROUTE table in configDB. @@ -180,6 +207,7 @@ module: sonic-static-route +--rw ifname* union +--rw distance* uint32 +--rw nexthop-vrf* string + +--rw blackhole* boolean ``` @@ -192,14 +220,14 @@ ipv6 route command is used to configure IPv6 static routes in SONiC. ##### 3.6.2.2.1 Syntax -Vrf and distance metric are optional in the CLI. +Vrf (for default instance) and distance metric are optional in the CLI. ``` -ip route vrf {[interface ] | [] | [ interface ]} +ip route [vrf ] {[interface ] | [] | [ interface ]| [blackhole]} [nexthop-vrf ] ``` ``` -ipv6 route vrf {[interface ] | [] | [ interface ]} +ipv6 route [vrf ] {[interface ] | [] | [ interface ]|[blackhole]} [nexthop-vrf ] ``` @@ -210,9 +238,11 @@ Syntax Description: |:-----------------:|:-----------:| | vrf | The static routes will be configured in the specified VRF. If not specified, default VRF will be used. | prefix | This is the destination prefix for IPv4 or IPv6 routes. -| interface | Specifies the interface to be used for this route. If the interface doesn't belong to the current VRF, it is considered a route-leak. The VRF to which this interface belongs will be set as the nexthop-vrf in the STATIC_ROUTE table . -| next-hop-ip | This provides the gateway IP for the prefix. -| distance Metric| Specifies distance value for this route. Value form 1 to 255. +| interface | Specifies the interface to be used for this route. Specify nexthop-vrf correctly if interface is in another VRF. +| next-hop-ip | This provides the gateway IP for the prefix. Specify nexthop-vrf correctly if this IP is in another VRF. +| distance Metric| Specifies distance value for this route. Value from 1 to 255. +|nexthop-vrf| Specifies nexthop-vrf if interface or next-hop-ip is in another VRF. +|blackhole| Specifies if this nexthop is to be blackholed. Only distance metric is relevant for blackhole. Example: @@ -229,11 +259,11 @@ nexthop = 10.1.1.3 ifname = "" distance = 0 nexthop-vrf = "" +blackhole = false -sonic(config)# ip route 10.1.1.1/24 Ethernet12 20 - -Assumption is Ethernet12 is configured in Vrf-RED by user. +sonic(config)# ip route 10.1.1.1/24 Ethernet12 nexthop-vrf Vrf-RED 20 +Assumption is Ethernet12 is bound to Vrf-RED. STATIC_ROUTE table entries are updated for newly added route:- key = STATIC_ROUTE|default|10.1.1.1/24; vrf-name = default @@ -241,22 +271,30 @@ prefix = 10.1.1.1/24 nexthop = 10.1.1.3, 0.0.0.0 ifname = "", Ethernet12 distance = 0, 20 -nexthop-vrf = "", Vrf-RED. +nexthop-vrf = "", Vrf-RED +blackhole = false, false + +Note: "show ip route" will also show installed static routes along with other routes. + +sonic(config)# do show ip route vrf ```` ```` -sonic(config)# ip route vrf Vrf-RED 10.5.6.6/24 Ethernet0 10 +sonic(config)# ip route vrf Vrf-RED 10.5.6.6/24 10.5.6.1 nexthop-vrf default 10 -Assumption is Ethernet0 is in default VRF. +Assumption is 10.5.6.1 is in default VRF. A new STATIC_ROUTE table will be filled with following entries for Vrf-RED and prefix:- key = STATIC_ROUTE|Vrf-RED|10.5.6.6/24; vrf-name = Vrf-RED prefix = 10.5.6.6/24 -nexthop = 0.0.0.0 -ifname = Ethernet0 +nexthop = 10.5.6.1 +ifname = "" distance = 10 nexthop-vrf = default +blackhole = false + + ```` @@ -265,18 +303,25 @@ nexthop-vrf = default sonic(config)# ipv6 route 2::/64 Ethernet16 sonic(config)# ipv6 route 2001:FF21:1:1::/64 18:2:1::1 100 + +sonic(config)# ipv6 route 2111:dddd:0eee::22/128 blackhole 200 + +Note: "show ipv6 route" will show static routes along with other routes. + +sonic(config)# do show ipv6 route vrf + ```` ##### 3.6.2.2.2 ipv4 /ipv6 Command to delete a next-hop. -The vrf is an optional parameter. +The vrf is an optional parameter for default instance. ``` -no ip route vrf {[interface ] | [] | [ interface ]} +no ip route vrf {[interface ] | [] | [ interface ]|[blackhole]} ``` ``` -no ipv6 route vrf {[interface ] | [] | [ interface ]} +no ipv6 route vrf {[interface ] | [] | [ interface ][blackhole]} ``` #### 3.6.2.3 Debug Commands #### 3.6.2.4 IS-CLI Compliance @@ -288,16 +333,19 @@ The following table maps SONiC CLI commands to corresponding IS-CLI commands. Th |CLI Command|Compliance|IS-CLI Command (if applicable)| Link to the web site identifying the IS-CLI command (if applicable)| |:---:|:-----------:|:------------------:|-----------------------------------| -|ip route |IS-CLI drop-in replace |Similar to OS-10 | | -|ipv6 route| IS-CLI drop-in replace | Similar to OS-10 | | -|no ip route |IS-CLI drop-in replace |Similar to OS-10 | | -|no ipv6 route |IS-CLI drop-in replace |Similar to OS-10 | | +|ip route |IS-CLI-like || | +|ipv6 route| IS-CLI-like| | | +|no ip route |IS-CLI-like || | +|no ipv6 route |IS-CLI-like| | | ### 3.6.3 REST API Support #### 3.6.3.1 -##### Get all support for both ARPs and Neighbors -Various REST operations (POST/PUT/PATCH/GET/DELETE) will be supported for the static route configuration. +##### Following REST operations will be supported + +1. GET ,POST, PATCH and DELETE supported at /restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol={identifier},{name1}/static-routes +2. GET , POST, PATCH and DELETE supported at /restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol={identifier},{name1}/static-routes/static={prefix} +3. GET, POST and DELETE supported at /restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol={identifier},{name1}/static-routes/static={prefix}/next-hops/next-hop={index} ## 3.7 FRR Configuration Support ### 3.7.1 Configuration Mapping to FRR @@ -313,12 +361,13 @@ vrf #### Mapping from table entry to FRR command arguments |Parameter Name|Table Entry Key or Field|Type|Default or Null Value| |:---:|:-----------:|:------------------:|-----------------------------------| -|src_vrf|first entry key*|optional|"default"| +|src_vrf|first entry key*|optional for default VRF|"default"| |ip_prefix|second entry key*|mandatory|-| -|nexthop_ip|entry field **nexthop****|optional |"0.0.0.0" or "::"***| -|interface|entry field **ifname****|optional |""| +|nexthop_ip|entry field **nexthop****|optional if ifname is set or blackhole is true |"0.0.0.0" or "::"***| +|interface|entry field **ifname****|optional if nexthop_ip is set or blackhole is true |""| |distance|entry field **distance****|optional|"0"| -|nexthop_vrf|entry field **nexthop-vrf****|optional|""| +|nexthop_vrf|entry field **nexthop-vrf****|required for route-leak case else optional |""| +|blackhole|entry field **blackhole***|required for blackhole next-hop else optional | false| Note: - *If table entry key contains only one item, it should be prefix_ip and src_vrf will be used with default value "default". @@ -337,6 +386,8 @@ Data in STATIC_ROUTE table: 6) "Ethernet0,Ethernet4" 7) "nexthop-vrf@" 8) "Vrf-BLUE,Vrf-RED" +9) "blackhole@" +10) "false,false" ``` FRR running config: ``` @@ -348,7 +399,7 @@ vrf Vrf-test ! ``` ### 3.7.2 Configuration Reload -All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon starts. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is be loaded by FRR daemon to configure static routes to system. +All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon starts. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is loaded by FRR daemon to configure static routes to system. # 4 Flow Diagrams @@ -362,6 +413,7 @@ All static route configurations were persistently stored in config DB STATIC_ROU # 9 Unit Test and automation The following test cases will be tested using CLI/REST/gNMI management interfaces. #### Static route Unit test cases: + 1) Verify creation deletion of multiple next-hops for a prefix list in default vrf. 2) Verify creation deletion of multiple next-hops for a prefix list in not-default vrf. 3) Verify creation deletion of multiple next-hops for a prefix list for route leak case where the specified interface belongs to a different VRF. From 09075ef5990067017f58ea06632c8a16bbbfc76b Mon Sep 17 00:00:00 2001 From: Sucheta Mahara Date: Wed, 13 May 2020 11:31:11 -0700 Subject: [PATCH 4/5] Review comments and test cases. --- .../SONiC_static_route_hdl.md | 140 +++++++++++------- doc/static-route/static_rt_flow.jpg | Bin 0 -> 221368 bytes 2 files changed, 85 insertions(+), 55 deletions(-) rename doc/{ => static-route}/SONiC_static_route_hdl.md (70%) create mode 100644 doc/static-route/static_rt_flow.jpg diff --git a/doc/SONiC_static_route_hdl.md b/doc/static-route/SONiC_static_route_hdl.md similarity index 70% rename from doc/SONiC_static_route_hdl.md rename to doc/static-route/SONiC_static_route_hdl.md index dabbd48fe5b..27e24741d1e 100644 --- a/doc/SONiC_static_route_hdl.md +++ b/doc/static-route/SONiC_static_route_hdl.md @@ -20,7 +20,7 @@ Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management |:---:|:-----------:|:---------------------------:|-----------------------------------| | 0.1 | 03/18/2020 | Sucheta Mahara | initial draft | | 0.2| 03/20/20202 | Venkatesan Mahalinga | draft | -| 0.2 | 03/23/2020 | Zhenhong Zhao | FRR Config Support | +| 0.3 | 03/23/2020 | Zhenhong Zhao | FRR Config Support | # About this Manual This document provides general information about configuring static routes via Management CLI/REST/gNMI in Sonic. @@ -51,19 +51,20 @@ Provide ability to configure IPv4 and IPv6 static routes using SONiC management - REST set/get support for Static IPv4 and IPv6 routes. - gNMI set/get support for Static IPv4 and IPv6 routes. -### 1.1.3 Scalability Requirements -### 1.1.4 Warm Boot Requirements +### 1.1.3 Warm Boot Requirements +With static routes configured system will be able to do warmboot. -## 1.2 Design Overview -### 1.2.1 Basic Approach -### 1.2.2 Container -### 1.2.3 SAI Overview # 2 Functionality ## 2.1 Target Deployment Use Cases +Use of sonic management framework to configure routes. ## 2.2 Functional Description +1.Provide CLI, gNMI and REST support for static route configuration. + # 3 Design ## 3.1 Overview An existing table STATIC_ROUTE (which is not used currently) will be used to write static route from the transformer for any CLI, rest or gNMI request. This table will be monitored by bgpcfgd daemon and the config will be sent to vtysh shell for configuring in FRR. + +![Static route flow](static_rt_flow.jpg) ## 3.2 DB Changes ### 3.2.1 CONFIG DB STATIC_ROUTE table in config DB will be used to support this feature. @@ -77,22 +78,22 @@ STATIC_ROUTE table in config DB will be used to support this feature. key = STATIC_ROUTE|vrf-name|prefix ; vrf-name = 1\*15VCHAR ; VRF name prefix = IPv4Prefix / IPv6prefix -nexthop = IPv4Address / IPv6Address; List of gateway addresses; -ifname = List of interfaces -distance = {0..255};List of distances. +nexthop = string; List of gateway addresses; +ifname = string; List of interfaces +distance = string; {0..255};List of distances. Its a Metric used to specify preference of next-hop if this distance is not set, 0 will be set to maintain the set; -nexthop-vrf = 1\*15VCHAR ; list of next-hop VRFs. It should be set only if ifname or nexthop IP is not +nexthop-vrf = string; list of next-hop VRFs. It should be set only if ifname or nexthop IP is not in the current VRF . The value is set to VRF name to which the interface or nexthop IP belongs for route leaks. -blackhole = List of boolean; true if the next-hop route is blackholed. +blackhole = string; List of boolean; true if the next-hop route is blackholed. ``` The nexthop-vrf if set, will allow to create a leaked route in the current VRF(vrf-name). The vrf-name, nexthop-vrf, prefix, ifname, distance and blackhole are all parameters required to configure static routes and route leaks in vtysh. -If an interface is moved from one VRF to another and it exist in the STATIC_ROUTE table, error will be returned by the backend. User is expected to remove all static routes pertaining to the interface before binding the interface to a different VRF. -If prefix is IPv4Prefix nexthop will have IPv4Address and if prefix is IPv6Prefix nexthop will have IPv6Address. -In this table each entry based on the index in the lists of nexthop, ifname, distance, nexthop-vrf and blackhole is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. +If an interface is moved from one VRF to another and it exist in the STATIC_ROUTE table, the configured routes with that interface will become inactive. These routes are stored in vtysh "running config" and STATIC_ROUTE table. It is up to the user to remove stale/inactive routes. They will become active if/when the interface is bound again to the orignal VRF. -In the example -1 table below (0.0.0.0, Ethernet0, 10, default, false) , (2.2.2.2, "", 0,"", false), (0.0.0.0, Ethernet12, 30, Vrf-GREEN, false) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. +In this table each entry based on the index in the lists of nexthop, ifname, distance, nexthop-vrf and blackhole is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. Prefix and nexthop in a set is expected to have same address family. + +In the example -1 table below (0.0.0.0, Ethernet0, 10, default, false) , (2.2.2.1, "", 0,"", false), (0.0.0.0, Ethernet12, 30, Vrf-GREEN, false) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. In example -2 the blackhole is true for second index and only distance is relevant in blackholed route. Due to blackhole entry (with metric 40) all packets matching 10.12.11.0/24 will be discarded if other specified nexthop is inactive. ```JSON @@ -102,17 +103,17 @@ key = STATIC_ROUTE|Vrf-RED|10.11.1.0/24; vrf-name = Vrf-RED prefix = 10.11.1.0/24 nexthop = 0.0.0.0, 2.2.2.1, 0.0.0.0 -ifname = "Ethernet0", "", "Ethernet12" +ifname = Ethernet0,"", Ethernet12 distance = 10,0,30 -nexthop-vrf = "default", "" , "Vrf-GREEN" +nexthop-vrf = default,"",Vrf-GREEN blackhole = false, false, false EXAMPLE 2:- key = STATIC_ROUTE|default|10.12.11.0/24; vrf-name = default prefix = 10.12.11.0/24 -nexthop = 2.2.2.3, "" -ifname = "", "" +nexthop = 2.2.2.3,"" +ifname = "","" distance = 10,40 nexthop-vrf = "","" blackhole = false, true @@ -125,15 +126,15 @@ Note: This model is proposed in line with ROUTE_TABLE model in application DB us ### 3.2.2 APP DB -### 3.2.3 STATE DB -### 3.2.4 ASIC DB -### 3.2.5 COUNTER DB +A new table "UNRESOLVE_NEIGH_TABLE" in aplication DB will be used to write unresolved next-hop for staic routes. This table will be written by router/neighbor module of orchagent and consumed by nbrmgrd process. Once the next-hop resolution is done by sending netlink message or static route is removed, entry is removed from the "UNRESOLVE_NEIGH_TABLE". These changes are coming from Broadcom. ## 3.3 Switch State Service Design ### 3.3.1 Orchestration Agent -### 3.3.2 Other Process +Refer to section 3.2.2 ## 3.4 SyncD +No change ## 3.5 SAI +No change ## 3.6 User Interface ### 3.6.1 Data Models @@ -190,7 +191,7 @@ New Parameters oc-local-routing-ext:nexthop-vrf-name|Specify the nexthop-vrf incase interface or next-hop is not in current instance(i.e VRF). This parameter is explicitly required by vtysh to correctly install route leaks. oc-local-routing-ext:blackhole | If set to true the nexhop is a blackholed route. Only metric is relevant when configuring blackhole next-hop for a prefix. -Note: Each next-hop entry has a key called "index" in openconfig yang as shown above. This key is used to uniquely identify a entry in the list of next-hops. This key will be formed based on "interface (interface name if present) + next-hop (next-hop-ip if present) + vrf-name" and is expected to be provided correctly by the user for non-blackhole routes. For blackhole route DROP + vrf-name will be used as key. Either interface name or next-hop-ip or both or blackhole should be present for any route to be accessed. i.e Ethernet0_2.2.2.2_default, 3.3.3.3_Vrf-RED, DROP_Vrf-GREEN. This key will not be stored in config DB. For default instance vrf-name in the key is optional. +Note: Each next-hop entry has a key called "index" in openconfig yang as shown above. This key is used to uniquely identify a entry in the list of next-hops. This key will be formed based on "interface (interface name if present) + next-hop (next-hop-ip if present) + next-hop vrf-name(if present)" and is expected to be provided correctly by the user for non-blackhole routes. For blackhole route DROP will be used as key. Either interface name or next-hop-ip or both or blackhole should be present for any route to be accessed. i.e Ethernet0_2.2.2.2_default, 3.3.3.3_Vrf-RED, DROP. This key will not be stored in config DB. A new sonic-static-route.yang is defined to store entries in STATIC_ROUTE table in configDB. @@ -203,11 +204,11 @@ module: sonic-static-route +--rw STATIC_ROUTE_LIST* [vrf-name prefix] +--rw vrf-name union +--rw prefix inet:ip-prefix - +--rw nexthop* inet:ip-address - +--rw ifname* union - +--rw distance* uint32 - +--rw nexthop-vrf* string - +--rw blackhole* boolean + +--rw nexthop string + +--rw ifname string + +--rw distance string + +--rw nexthop-vrf string + +--rw blackhole string ``` @@ -223,11 +224,11 @@ Syntax Vrf (for default instance) and distance metric are optional in the CLI. ``` -ip route [vrf ] {[interface ] | [] | [ interface ]| [blackhole]} [nexthop-vrf ] +ip route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] ``` ``` -ipv6 route [vrf ] {[interface ] | [] | [ interface ]|[blackhole]} [nexthop-vrf ] +ipv6 route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] ``` @@ -262,7 +263,7 @@ nexthop-vrf = "" blackhole = false -sonic(config)# ip route 10.1.1.1/24 Ethernet12 nexthop-vrf Vrf-RED 20 +sonic(config)# ip route 10.1.1.1/24 Ethernet 12 nexthop-vrf Vrf-RED 20 Assumption is Ethernet12 is bound to Vrf-RED. STATIC_ROUTE table entries are updated for newly added route:- key = STATIC_ROUTE|default|10.1.1.1/24; @@ -300,7 +301,7 @@ blackhole = false ```` #### IPv6 examples ```` -sonic(config)# ipv6 route 2::/64 Ethernet16 +sonic(config)# ipv6 route 2::/64 Ethernet 16 sonic(config)# ipv6 route 2001:FF21:1:1::/64 18:2:1::1 100 @@ -321,7 +322,7 @@ no ip route vrf {[interface ] ``` ``` -no ipv6 route vrf {[interface ] | [] | [ interface ][blackhole]} +no ipv6 route vrf {[interface ] | [] | [ interface ][blackhole]} ``` #### 3.6.2.3 Debug Commands #### 3.6.2.4 IS-CLI Compliance @@ -356,7 +357,7 @@ FRR vtysh command is composed with VRF/IP_prefix and nexthop data fields in STAT ``` configure terminal vrf -[no ]ip route [] [] [] [nexthop-vrf ] +[no ]ip route [] [][blackhole] [] [nexthop-vrf ] ``` #### Mapping from table entry to FRR command arguments |Parameter Name|Table Entry Key or Field|Type|Default or Null Value| @@ -401,22 +402,51 @@ vrf Vrf-test ### 3.7.2 Configuration Reload All static route configurations were persistently stored in config DB STATIC_ROUTE table. After BGP container restarts, the configuration in DB needs to be re-applied. This configuration reload is done by generating staticd.conf file before FRR staticd daemon starts. A jinja template file will be used to map table entries to fill in staticd.conf file. The generated conf file is loaded by FRR daemon to configure static routes to system. -# 4 Flow Diagrams - -# 5 Error Handling - -# 6 Serviceability and Debug -# 7 Warm Boot Support - -# 8 Scalability - -# 9 Unit Test and automation -The following test cases will be tested using CLI/REST/gNMI management interfaces. -#### Static route Unit test cases: - -1) Verify creation deletion of multiple next-hops for a prefix list in default vrf. -2) Verify creation deletion of multiple next-hops for a prefix list in not-default vrf. -3) Verify creation deletion of multiple next-hops for a prefix list for route leak case where the specified interface belongs to a different VRF. -4) Verify the get/set operation passes for a particular next-hop when supplying the correct index as key. -#### Automation -Spytest cases will be incremented for new CLI and APIs. +# 4 Unit Test and automation +The following test cases will be tested using CLI/REST/gNMI management interfaces and spytest will be incremeted. + +|Test Cases (done on default instance and VRF)| Test Result | +| :------ | :----- | +|create route for a prefix with NH IP addresses| verify the route is created| +|create route for a prefix with NH IP address and interface| verify route is created| +|create route for a prefix with interface only| verify route is created| +|create route for a prefix with NH IP addresses and NH VRF| verify the route is created| +|create route for a prefix with NH IP address, interface and NH VRF| verify route is created| +|create route for a prefix with interface and NH VRF| verify route is created| +|create route for a prefix with NH IP addresses and distance| verify the route is created| +|create route for a prefix with NH IP address, interface and distance | verify route is created| +|create route for a prefix with interface and distance | verify route is created| +|create route for a prefix with NH IP addresses, NH VRF and distane | verify the route is created| +|create route for a prefix with NH IP address, interface and distance| verify route is created| +|create route for a prefix with interface, NH VRF and distance | verify route is created| +|create route for a prefix with blackhole and distance |verify route is created +|CLI: show ip route| verify static routes are displayed with flag "S" (i.e Static)| +|Delete route for a prefix with NH IP addresses| verify the route is deleted| +|Delete route for a prefix with NH IP address and interface| verify route is deleted| +|Delete route for a prefix with interface only| verify route is deleted| +|Delete route for a prefix with NH IP addresses and NH VRF| verify the route is deleted| +|Delete route for a prefix with NH IP address, interface and NH VRF| verify route is deleted| +|Delete route for a prefix with interface and NH VRF| verify route is deleted| +|Delete route for a prefix with blackhole| verify route is deleted| +| ###Negative test cases ###| +|Try creating a route with mismatched prefix(IPV4) and address(IPV6)| test will fail| +|Try creating a route with wrong distance like 300| test will fail| +|Try creating a route with non-existing VRF| backened will fail| + +|Test Cases specific to REST/gNMI (done on default instance and VRF)| Test Result| +| :------ | :----- | +|Add single NH in a prefix| verify that is created| +|GET single NH in a prefix| verify the result with what was set| +|Delete single NH in a prefix| verify the route is deleted| +|Create multiple nexthop in a prefix| verify they are created| +|Use Get for a prefix with multiple next-hops|verify the output macthes with what was set| +|Delete a prefix | Verify all routes within the prefix are deleted| +|Create multiple prefix with multiple Next-hops in a VRF|verify all the routes are created| +|Use GET at static_routes level to get all the routes| verify the result with what was set| +|Write the configDB and reload the box| verify all the routes are configured correctly in FRR| +|Delete routes at static_routes level |verify all routes with the all prefixes are deleted| +|Try deleting a NH with wrong index key | failure returned| + +Note: NH = next hop ; +Verification of config will be done using vtysh FRR shell comparison. +sonic-swss/tests/test_route.py will be augmented to write to new SONIC_ROUTE configdb table directly. Static route configuration will be verified thereafter. diff --git a/doc/static-route/static_rt_flow.jpg b/doc/static-route/static_rt_flow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67551b8026b4c608730be3b180bd5354b02be406 GIT binary patch literal 221368 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 z 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

M!e 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%0A(6NpohPGaH?<;hWA^p$CD}JihU8g;7Lp0mq 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 Lfi?|vygz&ZRO>X! literal 0 HcmV?d00001 From 1e8551e0ffe6a01ac5a717491814943d37e29ad9 Mon Sep 17 00:00:00 2001 From: Venkatesan Mahalingam Date: Wed, 14 Apr 2021 04:42:04 +0000 Subject: [PATCH 5/5] Addressed the review comments. Signed-off-by: Venkatesan Mahalingam --- doc/static-route/SONiC_static_route_hdl.md | 162 +++++++++++++-------- 1 file changed, 100 insertions(+), 62 deletions(-) diff --git a/doc/static-route/SONiC_static_route_hdl.md b/doc/static-route/SONiC_static_route_hdl.md index 27e24741d1e..44b92c7e43c 100644 --- a/doc/static-route/SONiC_static_route_hdl.md +++ b/doc/static-route/SONiC_static_route_hdl.md @@ -3,7 +3,7 @@ Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management framework for non-management routes. # High Level Design Document -#### Rev 0.1 +#### Rev 0.4 # Table of Contents * [List of Tables](#list-of-tables) @@ -19,8 +19,9 @@ Implement Static IP route Configuration via CLI/REST/gNMI in SONiC management | Rev | Date | Author | Change Description | |:---:|:-----------:|:---------------------------:|-----------------------------------| | 0.1 | 03/18/2020 | Sucheta Mahara | initial draft | -| 0.2| 03/20/20202 | Venkatesan Mahalinga | draft | +| 0.2| 03/20/20202 | Venkatesan Mahalingam | draft | | 0.3 | 03/23/2020 | Zhenhong Zhao | FRR Config Support | +| 0.4 | 04/13/2021 | Sucheta Mahara & Venkatesan Mahalingam | Addressed community review comments | # About this Manual This document provides general information about configuring static routes via Management CLI/REST/gNMI in Sonic. @@ -52,17 +53,17 @@ Provide ability to configure IPv4 and IPv6 static routes using SONiC management - gNMI set/get support for Static IPv4 and IPv6 routes. ### 1.1.3 Warm Boot Requirements -With static routes configured system will be able to do warmboot. +With static routes configured, system will be able to do warmboot. # 2 Functionality ## 2.1 Target Deployment Use Cases Use of sonic management framework to configure routes. ## 2.2 Functional Description -1.Provide CLI, gNMI and REST support for static route configuration. +Provide CLI, gNMI and REST support for static route configuration. # 3 Design ## 3.1 Overview -An existing table STATIC_ROUTE (which is not used currently) will be used to write static route from the transformer for any CLI, rest or gNMI request. This table will be monitored by bgpcfgd daemon and the config will be sent to vtysh shell for configuring in FRR. +An existing table STATIC_ROUTE (which is not used currently) will be used to write static route from the transformer for any CLI, rest or gNMI request. This table will be monitored by frrcfgd daemon and the config will be sent to vtysh shell for configuring in FRR. ![Static route flow](static_rt_flow.jpg) ## 3.2 DB Changes @@ -82,16 +83,17 @@ nexthop = string; List of gateway addresses; ifname = string; List of interfaces distance = string; {0..255};List of distances. Its a Metric used to specify preference of next-hop - if this distance is not set, 0 will be set to maintain the set; + if this distance is not set, default value 0 will be set when this field is not configured for nexthop(s) nexthop-vrf = string; list of next-hop VRFs. It should be set only if ifname or nexthop IP is not - in the current VRF . The value is set to VRF name - to which the interface or nexthop IP belongs for route leaks. -blackhole = string; List of boolean; true if the next-hop route is blackholed. + in the current VRF . The value is set to VRF name + to which the interface or nexthop IP belongs for route leaks. +blackhole = string; List of boolean; true if the next-hop route is blackholed. + Default value false will be set when this field is not configured for nexthop(s) ``` The nexthop-vrf if set, will allow to create a leaked route in the current VRF(vrf-name). The vrf-name, nexthop-vrf, prefix, ifname, distance and blackhole are all parameters required to configure static routes and route leaks in vtysh. If an interface is moved from one VRF to another and it exist in the STATIC_ROUTE table, the configured routes with that interface will become inactive. These routes are stored in vtysh "running config" and STATIC_ROUTE table. It is up to the user to remove stale/inactive routes. They will become active if/when the interface is bound again to the orignal VRF. -In this table each entry based on the index in the lists of nexthop, ifname, distance, nexthop-vrf and blackhole is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string will be used to complete the set if required. Prefix and nexthop in a set is expected to have same address family. +In this table each entry based on the index in the lists of nexthop, ifname, distance, nexthop-vrf and blackhole is a set. Together based on the index they specify one next-hop entry as there can be multiple next-hops for a prefix. Empty string and default values will be used to complete the set if required. Prefix and nexthop in a set is expected to have same address family. In the example -1 table below (0.0.0.0, Ethernet0, 10, default, false) , (2.2.2.1, "", 0,"", false), (0.0.0.0, Ethernet12, 30, Vrf-GREEN, false) are the 3 sets defined for 3 configured next-hops for prefix 10.11.1.0/24 in Vrf-RED. In example -2 the blackhole is true for second index and only distance is relevant in blackholed route. Due to blackhole entry (with metric 40) all packets matching 10.12.11.0/24 will be discarded if other specified nexthop is inactive. @@ -100,22 +102,19 @@ In example -2 the blackhole is true for second index and only distance is rele Example -1 :-- key = STATIC_ROUTE|Vrf-RED|10.11.1.0/24; -vrf-name = Vrf-RED -prefix = 10.11.1.0/24 nexthop = 0.0.0.0, 2.2.2.1, 0.0.0.0 -ifname = Ethernet0,"", Ethernet12 +ifname = Ethernet0,"" , Ethernet12 distance = 10,0,30 nexthop-vrf = default,"",Vrf-GREEN -blackhole = false, false, false + +Note: + Field "blackhole or distance" need not be specified if it has all default values for the sets. False for blackhole and 0 for distance. + Field "nexthop or ifname or nexthop-vrf need not be specified if they have empty value in all the sets" EXAMPLE 2:- key = STATIC_ROUTE|default|10.12.11.0/24; -vrf-name = default -prefix = 10.12.11.0/24 nexthop = 2.2.2.3,"" -ifname = "","" distance = 10,40 -nexthop-vrf = "","" blackhole = false, true @@ -126,7 +125,7 @@ Note: This model is proposed in line with ROUTE_TABLE model in application DB us ### 3.2.2 APP DB -A new table "UNRESOLVE_NEIGH_TABLE" in aplication DB will be used to write unresolved next-hop for staic routes. This table will be written by router/neighbor module of orchagent and consumed by nbrmgrd process. Once the next-hop resolution is done by sending netlink message or static route is removed, entry is removed from the "UNRESOLVE_NEIGH_TABLE". These changes are coming from Broadcom. +A table "NEIGH_RESOLVE_TABLE" in application DB will be used to write unresolved next-hop for static routes. This table will be written by router/neighbor module of orchagent and consumed by nbrmgrd process. ## 3.3 Switch State Service Design ### 3.3.1 Orchestration Agent @@ -215,20 +214,19 @@ module: sonic-static-route ### 3.6.2 CLI #### 3.6.2.1 Configuration Commands -#### 3.6.2.2 ip/ipv6 route config command +#### 3.6.2.2 ipv4/ipv6 route config commands ip route command is used to configure IPv4 static routes in SONiC. ipv6 route command is used to configure IPv6 static routes in SONiC. -##### 3.6.2.2.1 -Syntax +##### 3.6.2.2.1 Syntax Vrf (for default instance) and distance metric are optional in the CLI. ``` -ip route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] +ip route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] [] ``` ``` -ipv6 route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] +ipv6 route [vrf ] {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] [] ``` @@ -253,52 +251,57 @@ Example: sonic(config)# ip route 10.1.1.1/24 10.1.1.3 In configDB new STATIC_ROUTE table will be filled with following entries for default VRF and prefix:- + key = STATIC_ROUTE|default|10.1.1.1/24; -vrf-name = default -prefix = 10.1.1.1/24 nexthop = 10.1.1.3 -ifname = "" -distance = 0 -nexthop-vrf = "" -blackhole = false - sonic(config)# ip route 10.1.1.1/24 Ethernet 12 nexthop-vrf Vrf-RED 20 + Assumption is Ethernet12 is bound to Vrf-RED. STATIC_ROUTE table entries are updated for newly added route:- + key = STATIC_ROUTE|default|10.1.1.1/24; -vrf-name = default -prefix = 10.1.1.1/24 nexthop = 10.1.1.3, 0.0.0.0 ifname = "", Ethernet12 distance = 0, 20 nexthop-vrf = "", Vrf-RED -blackhole = false, false - -Note: "show ip route" will also show installed static routes along with other routes. -sonic(config)# do show ip route vrf -```` - -```` sonic(config)# ip route vrf Vrf-RED 10.5.6.6/24 10.5.6.1 nexthop-vrf default 10 Assumption is 10.5.6.1 is in default VRF. A new STATIC_ROUTE table will be filled with following entries for Vrf-RED and prefix:- + key = STATIC_ROUTE|Vrf-RED|10.5.6.6/24; vrf-name = Vrf-RED prefix = 10.5.6.6/24 nexthop = 10.5.6.1 -ifname = "" distance = 10 nexthop-vrf = default -blackhole = false +```` + +``` +Note: "show ip route" will show installed static routes along with other routes. "show ip route static" can be used to display only static routes. All show information is fetched from FRR. +Syntax to show static routes + +show ip route [vrf ] [static] + +sonic# show ip route static +Codes: K - kernel route, C - connected, S - static, B - BGP, O - OSPF + > - selected route, * - FIB route, q - queued route, r - rejected route, + # - not installed in hardware + + Destination Gateway Dist/Metric Uptime + + S# 3.3.3.0/24 Direct Ethernet4 150/0 00:33:31 + S# 6.6.6.0/24 via 6.6.6.1 Ethernet0 1/0 00:00:03 + S# 7.7.7.0/24 via 7.7.7.1 Ethernet12 36/0 00:01:56 + +``` -```` #### IPv6 examples ```` sonic(config)# ipv6 route 2::/64 Ethernet 16 @@ -307,38 +310,33 @@ sonic(config)# ipv6 route 2001:FF21:1:1::/64 18:2:1::1 100 sonic(config)# ipv6 route 2111:dddd:0eee::22/128 blackhole 200 -Note: "show ipv6 route" will show static routes along with other routes. +Note: "show ipv6 route " will show static routes along with other routes. This information will be fetched from FRR. + To see only static routes use "show ipv6 route [vrf ] [static]" sonic(config)# do show ipv6 route vrf ```` -##### 3.6.2.2.2 ipv4 /ipv6 Command to delete a next-hop. +##### 3.6.2.2.2 ipv4 /ipv6 Command to delete a static route. The vrf is an optional parameter for default instance. ``` -no ip route vrf {[interface ] | [] | [ interface ]|[blackhole]} +no ip route vrf {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] -``` ``` -no ipv6 route vrf {[interface ] | [] | [ interface ][blackhole]} -``` -#### 3.6.2.3 Debug Commands -#### 3.6.2.4 IS-CLI Compliance -The following table maps SONiC CLI commands to corresponding IS-CLI commands. The compliance column identifies how the command comply to the IS-CLI syntax: -- **IS-CLI drop-in replace** \u2013 meaning that it follows exactly the format of a pre-existing IS-CLI command. -- **IS-CLI-like** \u2013 meaning that the exact format of the IS-CLI command could not be followed, but the command is similar to other commands for IS-CLI (e.g. IS-CLI may not offer the exact option, but the command can be positioned is a similar manner as others for the related feature). -- **SONiC** - meaning that no IS-CLI-like command could be found, so the command is derived specifically for SONiC. +``` +no ipv6 route vrf {[interface ] | [] | [ interface ] | [blackhole]} [nexthop-vrf ] -|CLI Command|Compliance|IS-CLI Command (if applicable)| Link to the web site identifying the IS-CLI command (if applicable)| -|:---:|:-----------:|:------------------:|-----------------------------------| -|ip route |IS-CLI-like || | -|ipv6 route| IS-CLI-like| | | -|no ip route |IS-CLI-like || | -|no ipv6 route |IS-CLI-like| | | +``` +##### CLI examples to delete static routes +```` +sonic(config)# no ip route 10.1.1.1/24 Ethernet 12 nexthop-vrf Vrf-RED +sonic(config)# no ipv6 route 2::/64 Ethernet 16 +sonic(config)# no ipv6 route 2001:FF21:1:1::/64 18:2:1::1 +```` ### 3.6.3 REST API Support #### 3.6.3.1 @@ -348,9 +346,49 @@ The following table maps SONiC CLI commands to corresponding IS-CLI commands. Th 2. GET , POST, PATCH and DELETE supported at /restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol={identifier},{name1}/static-routes/static={prefix} 3. GET, POST and DELETE supported at /restconf/data/openconfig-network-instance:network-instances/network-instance={name}/protocols/protocol={identifier},{name1}/static-routes/static={prefix}/next-hops/next-hop={index} +Example of URI:- +``` +# curl -X PATCH "https://192.168.1.1/restconf/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" -H "accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d "{\"openconfig-network-instance:static-routes\": {\"static\": [{\"next-hops\": {\"next-hop\": [{\"index\": \"DROP\", \"config\": {\"index\": \"DROP\", \"blackhole\": true}}]}, \"prefix\": \"21.0.0.0/8\"}]}}" -u admin:xxxxx -k + +# curl -kX GET "https://192.168.1.1/restconf/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=STATIC,static/static-routes" -H "accept: application/yang-data+json" -u admin:xxxxx -k | python3 -m json.tool + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 334 100 334 0 0 1397 0 --:--:-- --:--:-- --:--:-- 1409 +{ + "openconfig-network-instance:static-routes": { + "static": [ + { + "config": { + "prefix": "21.0.0.0/8" + }, + "next-hops": { + "next-hop": [ + { + "config": { + "index": "DROP", + "openconfig-local-routing-ext:blackhole": true + }, + "index": "DROP", + "state": { + "index": "DROP", + "openconfig-local-routing-ext:blackhole": true + } + } + ] + }, + "prefix": "21.0.0.0/8", + "state": { + "prefix": "21.0.0.0/8" + } + } + ] + } +} +``` + ## 3.7 FRR Configuration Support ### 3.7.1 Configuration Mapping to FRR -Bgpcfgd daemon will be used to forward configurations stored in STATIC_ROUTE table to FRR staticd daemon. It will subscribe to listen to STATIC_ROUTE table and if there is data update, it will convert associated data to FRR vtysh command request and send to FRR daemon to configure static route on Linux kernel. +frrcfgd daemon will be used to forward configurations stored in STATIC_ROUTE table to FRR staticd daemon. It will subscribe to listen to STATIC_ROUTE table and if there is data update, it will convert associated data to FRR vtysh command request and send to FRR daemon to configure static route on Linux kernel. #### 3.7.1.1 Table entry to command mapping FRR vtysh command is composed with VRF/IP_prefix and nexthop data fields in STATIC_ROUTE table entry #### FRR command syntax