@@ -60,13 +60,12 @@ struct route_show_ctx {
6060 bool header_done ; /* common header already displayed */
6161};
6262
63- static int do_show_ip_route (struct vty * vty , const char * vrf_name , afi_t afi ,
64- safi_t safi , bool use_fib , bool use_json ,
65- route_tag_t tag ,
66- const struct prefix * longer_prefix_p ,
67- bool supernets_only , int type ,
68- unsigned short ospf_instance_id , uint32_t tableid ,
69- bool show_ng , struct route_show_ctx * ctx );
63+ static int do_show_ip_route (struct vty * vty , const char * vrf_name , afi_t afi , safi_t safi ,
64+ bool use_fib , bool use_json , route_tag_t tag ,
65+ const struct prefix * longer_prefix_p , bool supernets_only , int type ,
66+ unsigned short ospf_instance_id , uint32_t tableid , bool show_ng ,
67+ bool show_nhg_summary , bool ecmp_gt , bool ecmp_lt , bool ecmp_eq ,
68+ uint16_t ecmp_count , struct route_show_ctx * ctx );
7069static void vty_show_ip_route_detail (struct vty * vty , struct route_node * rn ,
7170 int mcast , bool use_fib , bool show_ng );
7271static void vty_show_ip_route_summary (struct vty * vty , struct route_table * table ,
@@ -507,7 +506,8 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
507506}
508507
509508static void vty_show_ip_route (struct vty * vty , struct route_node * rn , struct route_entry * re ,
510- json_object * json , bool is_fib , bool show_ng , bool show_nhg_summary )
509+ json_object * json , bool is_fib , bool show_ng , bool show_nhg_summary ,
510+ bool ecmp_gt , bool ecmp_lt , bool ecmp_eq , uint16_t ecmp_count )
511511{
512512 const struct nexthop * nexthop ;
513513 int len = 0 ;
@@ -531,6 +531,18 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct rou
531531 else
532532 nhg = & (re -> nhe -> nhg );
533533
534+ /* Apply ECMP count filter if specified */
535+ if (show_nhg_summary && (ecmp_gt || ecmp_lt || ecmp_eq )) {
536+ uint16_t nh_count = nexthop_group_nexthop_num_no_recurse (nhg );
537+
538+ if (ecmp_gt && nh_count <= ecmp_count )
539+ return ;
540+ if (ecmp_lt && nh_count >= ecmp_count )
541+ return ;
542+ if (ecmp_eq && nh_count != ecmp_count )
543+ return ;
544+ }
545+
534546 if (json ) {
535547 json_route = json_object_new_object ();
536548
@@ -570,7 +582,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct rou
570582
571583 /* NHG Summary JSON output */
572584 if (show_nhg_summary ) {
573- uint16_t ecmp_count = nexthop_group_nexthop_num_no_recurse (nhg );
585+ uint16_t nh_ecmp_count = nexthop_group_nexthop_num_no_recurse (nhg );
574586 uint16_t fib_nh_count = nexthop_group_fib_nexthop_num (nhg );
575587
576588 if (re -> tag )
@@ -579,8 +591,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct rou
579591 if (re -> table )
580592 json_object_int_add (json_route , "table" , re -> table );
581593
582- json_object_int_add (json_route , "nextHopGroupId" , re -> nhe_id );
583- json_object_int_add (json_route , "ecmpCount" , ecmp_count );
594+ json_object_int_add (json_route , "nextHopGroupId" , re -> nhe_id );
595+ json_object_int_add (json_route , "ecmpCount" , nh_ecmp_count );
584596 json_object_int_add (json_route , "fibInstalledCount" , fib_nh_count );
585597
586598 if (re -> nhe_installed_id != 0 )
@@ -706,13 +718,13 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct rou
706718
707719 /* Show ECMP summary instead of full nexthop details */
708720 if (show_nhg_summary ) {
709- uint16_t ecmp_count = nexthop_group_nexthop_num_no_recurse (nhg );
721+ uint16_t nh_ecmp_count = nexthop_group_nexthop_num_no_recurse (nhg );
710722 uint16_t fib_nh_count = nexthop_group_fib_nexthop_num (nhg );
711723 uint32_t ins_id = re -> nhe_installed_id ? re -> nhe_installed_id : re -> nhe_id ;
712724 uint32_t rcv_id = re -> nhe_received ? re -> nhe_received -> id : re -> nhe_id ;
713725
714726 vty_out (vty , " Rcv/Ins NHG ID: %u/%u" , rcv_id , ins_id );
715- vty_out (vty , " ECMP/FIB count: %u/%u" , ecmp_count , fib_nh_count );
727+ vty_out (vty , " ECMP/FIB count: %u/%u" , nh_ecmp_count , fib_nh_count );
716728 if (CHECK_FLAG (re -> status , ROUTE_ENTRY_INSTALLED ) ||
717729 CHECK_FLAG (re -> status , ROUTE_ENTRY_QUEUED ) ||
718730 CHECK_FLAG (re -> status , ROUTE_ENTRY_FAILED )) {
@@ -820,7 +832,8 @@ static void vty_show_ip_route_detail_json(struct vty *vty,
820832 */
821833 if (use_fib && re != dest -> selected_fib )
822834 continue ;
823- vty_show_ip_route (vty , rn , re , json_prefix , use_fib , false, false);
835+ vty_show_ip_route (vty , rn , re , json_prefix , use_fib , false, false, false, false,
836+ false, 0 );
824837
825838 /* Add flags and status to the last object */
826839 json_object * json_route =
@@ -866,7 +879,8 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
866879 route_tag_t tag , const struct prefix * longer_prefix_p ,
867880 bool supernets_only , int type , unsigned short ospf_instance_id ,
868881 bool use_json , uint32_t tableid , bool show_ng ,
869- bool show_nhg_summary , struct route_show_ctx * ctx )
882+ bool show_nhg_summary , bool ecmp_gt , bool ecmp_lt , bool ecmp_eq ,
883+ uint16_t ecmp_count , struct route_show_ctx * ctx )
870884{
871885 struct route_node * rn ;
872886 struct route_entry * re ;
@@ -944,13 +958,18 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
944958 }
945959
946960 vty_show_ip_route (vty , rn , re , json_prefix , use_fib , show_ng ,
947- show_nhg_summary );
961+ show_nhg_summary , ecmp_gt , ecmp_lt , ecmp_eq , ecmp_count );
948962 }
949963
950964 if (json_prefix ) {
951- prefix2str (& rn -> p , buf , sizeof (buf ));
952- vty_json_key (vty , buf , & first_json );
953- vty_json_no_pretty (vty , json_prefix );
965+ /* Only output if array has elements */
966+ if (json_object_array_length (json_prefix ) > 0 ) {
967+ prefix2str (& rn -> p , buf , sizeof (buf ));
968+ vty_json_key (vty , buf , & first_json );
969+ vty_json_no_pretty (vty , json_prefix );
970+ } else {
971+ json_object_put (json_prefix );
972+ }
954973
955974 json_prefix = NULL ;
956975 }
@@ -964,7 +983,8 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, afi_t
964983 bool use_fib , bool use_json , route_tag_t tag ,
965984 const struct prefix * longer_prefix_p , bool supernets_only ,
966985 int type , unsigned short ospf_instance_id , bool show_ng ,
967- bool show_nhg_summary , struct route_show_ctx * ctx )
986+ bool show_nhg_summary , bool ecmp_gt , bool ecmp_lt , bool ecmp_eq ,
987+ uint16_t ecmp_count , struct route_show_ctx * ctx )
968988{
969989 struct zebra_router_table * zrt ;
970990 struct rib_table_info * info ;
@@ -980,15 +1000,17 @@ static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, afi_t
9801000
9811001 do_show_ip_route (vty , zvrf_name (zvrf ), afi , safi , use_fib , use_json , tag ,
9821002 longer_prefix_p , supernets_only , type , ospf_instance_id ,
983- zrt -> tableid , show_ng , show_nhg_summary , ctx );
1003+ zrt -> tableid , show_ng , show_nhg_summary , ecmp_gt , ecmp_lt ,
1004+ ecmp_eq , ecmp_count , ctx );
9841005 }
9851006}
9861007
9871008static int do_show_ip_route (struct vty * vty , const char * vrf_name , afi_t afi , safi_t safi ,
9881009 bool use_fib , bool use_json , route_tag_t tag ,
9891010 const struct prefix * longer_prefix_p , bool supernets_only , int type ,
9901011 unsigned short ospf_instance_id , uint32_t tableid , bool show_ng ,
991- bool show_nhg_summary , struct route_show_ctx * ctx )
1012+ bool show_nhg_summary , bool ecmp_gt , bool ecmp_lt , bool ecmp_eq ,
1013+ uint16_t ecmp_count , struct route_show_ctx * ctx )
9921014{
9931015 struct route_table * table ;
9941016 struct zebra_vrf * zvrf = NULL ;
@@ -1021,7 +1043,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, sa
10211043
10221044 do_show_route_helper (vty , zvrf , table , afi , safi , use_fib , tag , longer_prefix_p ,
10231045 supernets_only , type , ospf_instance_id , use_json , tableid , show_ng ,
1024- show_nhg_summary , ctx );
1046+ show_nhg_summary , ecmp_gt , ecmp_lt , ecmp_eq , ecmp_count , ctx );
10251047
10261048 return CMD_SUCCESS ;
10271049}
@@ -1708,7 +1730,7 @@ DEFPY (show_route,
17081730 }]\
17091731 [" FRR_IP6_REDIST_STR_ZEBRA "$type_str]\
17101732 >\
1711- [nexthop-group$ng [summary$ng_summary]] [json$json]" ,
1733+ [nexthop-group$ng [summary$ng_summary [ecmp-count <gt$ecmp_gt|lt$ecmp_lt|eq$ecmp_eq> (1-256)$ecmp_count] ]] [json$json]" ,
17121734 SHOW_STR
17131735 IP_STR
17141736 "IP forwarding table\n"
@@ -1739,9 +1761,14 @@ DEFPY (show_route,
17391761 "IPv6 prefix\n"
17401762 "Show route matching the specified Network/Mask pair only\n"
17411763 FRR_IP6_REDIST_HELP_STR_ZEBRA
1742- JSON_STR
17431764 "Nexthop Group Information\n"
1744- "Show ECMP count summary\n" )
1765+ "Show ECMP count summary\n"
1766+ "Filter by ECMP count\n"
1767+ "Greater than (>)\n"
1768+ "Less than (<)\n"
1769+ "Equal to (=)\n"
1770+ "ECMP count value\n"
1771+ JSON_STR )
17451772{
17461773 afi_t afi = ipv4 ? AFI_IP : AFI_IP6 ;
17471774 safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST ;
@@ -1787,12 +1814,16 @@ DEFPY (show_route,
17871814 do_show_ip_route_all (vty , zvrf , afi , safi , !!fib , !!json ,
17881815 tag , prefix_str ? prefix : NULL ,
17891816 !!supernets_only , type ,
1790- ospf_instance_id , !!ng , true, & ctx );
1817+ ospf_instance_id , !!ng , true,
1818+ !!ecmp_gt , !!ecmp_lt , !!ecmp_eq ,
1819+ ecmp_count ? ecmp_count : 0 , & ctx );
17911820 else
17921821 do_show_ip_route (vty , zvrf_name (zvrf ), afi , safi , !!fib ,
17931822 !!json , tag , prefix_str ? prefix : NULL ,
17941823 !!supernets_only , type , ospf_instance_id ,
1795- table , false, true, & ctx );
1824+ table , false, true, !!ecmp_gt , !!ecmp_lt ,
1825+ !!ecmp_eq , ecmp_count ? ecmp_count : 0 ,
1826+ & ctx );
17961827 }
17971828 if (json )
17981829 vty_json_close (vty , first_vrf_json );
@@ -1814,11 +1845,15 @@ DEFPY (show_route,
18141845 if (table_all )
18151846 do_show_ip_route_all (vty , zvrf , afi , safi , !!fib , !!json , tag ,
18161847 prefix_str ? prefix : NULL , !!supernets_only ,
1817- type , ospf_instance_id , !!ng , true, & ctx );
1848+ type , ospf_instance_id , !!ng , true, !!ecmp_gt ,
1849+ !!ecmp_lt , !!ecmp_eq ,
1850+ ecmp_count ? ecmp_count : 0 , & ctx );
18181851 else
18191852 do_show_ip_route (vty , vrf -> name , afi , safi , !!fib , !!json , tag ,
18201853 prefix_str ? prefix : NULL , !!supernets_only ,
1821- type , ospf_instance_id , table , false, true, & ctx );
1854+ type , ospf_instance_id , table , false, true,
1855+ !!ecmp_gt , !!ecmp_lt , !!ecmp_eq ,
1856+ ecmp_count ? ecmp_count : 0 , & ctx );
18221857 }
18231858
18241859 return CMD_SUCCESS ;
@@ -1836,11 +1871,13 @@ DEFPY (show_route,
18361871 if (table_all )
18371872 do_show_ip_route_all (vty , zvrf , afi , safi , !!fib , !!json , tag ,
18381873 prefix_str ? prefix : NULL , !!supernets_only ,
1839- type , ospf_instance_id , !!ng , false, & ctx );
1874+ type , ospf_instance_id , !!ng , false, false,
1875+ false, false, 0 , & ctx );
18401876 else
18411877 do_show_ip_route (vty , zvrf_name (zvrf ), afi , safi , !!fib , !!json ,
18421878 tag , prefix_str ? prefix : NULL , !!supernets_only ,
1843- type , ospf_instance_id , table , !!ng , false, & ctx );
1879+ type , ospf_instance_id , table , !!ng , false, false,
1880+ false, false, 0 , & ctx );
18441881 }
18451882 if (json )
18461883 vty_json_close (vty , first_vrf_json );
@@ -1862,11 +1899,13 @@ DEFPY (show_route,
18621899 if (table_all )
18631900 do_show_ip_route_all (vty , zvrf , afi , safi , !!fib , !!json , tag ,
18641901 prefix_str ? prefix : NULL , !!supernets_only , type ,
1865- ospf_instance_id , !!ng , false, & ctx );
1902+ ospf_instance_id , !!ng , false, false, false, false, 0 ,
1903+ & ctx );
18661904 else
18671905 do_show_ip_route (vty , vrf -> name , afi , safi , !!fib , !!json , tag ,
18681906 prefix_str ? prefix : NULL , !!supernets_only , type ,
1869- ospf_instance_id , table , !!ng , false, & ctx );
1907+ ospf_instance_id , table , !!ng , false, false, false, false,
1908+ 0 , & ctx );
18701909 }
18711910
18721911 return CMD_SUCCESS ;
0 commit comments