back to topotato report
topotato coverage report
Current view: top level - lib - nexthop_group.c (source / functions) Hit Total Coverage
Test: test_bgp_distance_change.py::BGPDistanceChange Lines: 105 646 16.3 %
Date: 2023-02-24 18:37:13 Functions: 19 53 35.8 %

          Line data    Source code
       1             : /*
       2             :  * Nexthop Group structure definition.
       3             :  * Copyright (C) 2018 Cumulus Networks, Inc.
       4             :  *                    Donald Sharp
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the Free
       8             :  * Software Foundation; either version 2 of the License, or (at your option)
       9             :  * any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      12             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      14             :  * more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : #include <zebra.h>
      21             : 
      22             : #include <vrf.h>
      23             : #include <sockunion.h>
      24             : #include <nexthop.h>
      25             : #include <nexthop_group.h>
      26             : #include <nexthop_group_private.h>
      27             : #include <vty.h>
      28             : #include <command.h>
      29             : #include <jhash.h>
      30             : 
      31             : #include "lib/nexthop_group_clippy.c"
      32             : 
      33          12 : DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
      34             : 
      35             : /*
      36             :  * Internal struct used to hold nhg config strings
      37             :  */
      38             : struct nexthop_hold {
      39             :         char *nhvrf_name;
      40             :         union sockunion *addr;
      41             :         char *intf;
      42             :         bool onlink;
      43             :         char *labels;
      44             :         uint32_t weight;
      45             :         char *backup_str;
      46             : };
      47             : 
      48             : struct nexthop_group_hooks {
      49             :         void (*new)(const char *name);
      50             :         void (*modify)(const struct nexthop_group_cmd *nhgc);
      51             :         void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
      52             :                             const struct nexthop *nhop);
      53             :         void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
      54             :                             const struct nexthop *nhop);
      55             :         void (*delete)(const char *name);
      56             : };
      57             : 
      58             : static struct nexthop_group_hooks nhg_hooks;
      59             : 
      60             : static inline int
      61             : nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
      62             :                           const struct nexthop_group_cmd *nhgc2);
      63           0 : RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
      64             :             nexthop_group_cmd_compare)
      65             : 
      66             : static struct nhgc_entry_head nhgc_entries;
      67             : 
      68             : static inline int
      69           0 : nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
      70             :                           const struct nexthop_group_cmd *nhgc2)
      71             : {
      72           0 :         return strcmp(nhgc1->name, nhgc2->name);
      73             : }
      74             : 
      75          30 : static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
      76             : {
      77          30 :         struct nexthop *nexthop = nhg->nexthop;
      78             : 
      79          30 :         while (nexthop && nexthop->next)
      80             :                 nexthop = nexthop->next;
      81             : 
      82          30 :         return nexthop;
      83             : }
      84             : 
      85           1 : uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
      86             : {
      87           1 :         struct nexthop *nhop;
      88           1 :         uint8_t num = 0;
      89             : 
      90           2 :         for (ALL_NEXTHOPS_PTR(nhg, nhop))
      91           1 :                 num++;
      92             : 
      93           1 :         return num;
      94             : }
      95             : 
      96          30 : uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
      97             : {
      98          30 :         struct nexthop *nhop;
      99          30 :         uint8_t num = 0;
     100             : 
     101          60 :         for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
     102          30 :                 num++;
     103             : 
     104          30 :         return num;
     105             : }
     106             : 
     107          31 : uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
     108             : {
     109          31 :         struct nexthop *nhop;
     110          31 :         uint8_t num = 0;
     111             : 
     112          62 :         for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
     113          31 :                 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
     114          31 :                         num++;
     115             :         }
     116             : 
     117          31 :         return num;
     118             : }
     119             : 
     120             : uint8_t
     121           0 : nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
     122             : {
     123           0 :         struct nexthop *nhop;
     124           0 :         uint8_t num = 0;
     125             : 
     126           0 :         for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
     127           0 :                 if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
     128           0 :                         num++;
     129             :         }
     130             : 
     131           0 :         return num;
     132             : }
     133             : 
     134           0 : struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
     135             :                                const struct nexthop *nh)
     136             : {
     137           0 :         struct nexthop *nexthop;
     138             : 
     139           0 :         for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
     140           0 :                 if (nexthop_same(nh, nexthop))
     141           0 :                         return nexthop;
     142             :         }
     143             : 
     144             :         return NULL;
     145             : }
     146             : 
     147             : /*
     148             :  * Helper that locates a nexthop in an nhg config list. Note that
     149             :  * this uses a specific matching / equality rule that's different from
     150             :  * the complete match performed by 'nexthop_same()'.
     151             :  */
     152           0 : static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
     153             :                                    const struct nexthop *nh)
     154             : {
     155           0 :         struct nexthop *nexthop;
     156           0 :         int ret;
     157             : 
     158             :         /* We compare: vrf, gateway, and interface */
     159             : 
     160           0 :         for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
     161             : 
     162             :                 /* Compare vrf and type */
     163           0 :                 if (nexthop->vrf_id != nh->vrf_id)
     164           0 :                         continue;
     165           0 :                 if (nexthop->type != nh->type)
     166           0 :                         continue;
     167             : 
     168             :                 /* Compare gateway */
     169           0 :                 switch (nexthop->type) {
     170           0 :                 case NEXTHOP_TYPE_IPV4:
     171             :                 case NEXTHOP_TYPE_IPV6:
     172           0 :                         ret = nexthop_g_addr_cmp(nexthop->type,
     173           0 :                                                  &nexthop->gate, &nh->gate);
     174           0 :                         if (ret != 0)
     175           0 :                                 continue;
     176             :                         break;
     177           0 :                 case NEXTHOP_TYPE_IPV4_IFINDEX:
     178             :                 case NEXTHOP_TYPE_IPV6_IFINDEX:
     179           0 :                         ret = nexthop_g_addr_cmp(nexthop->type,
     180           0 :                                                  &nexthop->gate, &nh->gate);
     181           0 :                         if (ret != 0)
     182           0 :                                 continue;
     183             :                         /* Intentional Fall-Through */
     184             :                 case NEXTHOP_TYPE_IFINDEX:
     185           0 :                         if (nexthop->ifindex != nh->ifindex)
     186           0 :                                 continue;
     187             :                         break;
     188           0 :                 case NEXTHOP_TYPE_BLACKHOLE:
     189           0 :                         if (nexthop->bh_type != nh->bh_type)
     190           0 :                                 continue;
     191             :                         break;
     192             :                 }
     193             : 
     194             :                 return nexthop;
     195             :         }
     196             : 
     197             :         return NULL;
     198             : }
     199             : 
     200             : static bool
     201          15 : nexthop_group_equal_common(const struct nexthop_group *nhg1,
     202             :                            const struct nexthop_group *nhg2,
     203             :                            uint8_t (*nexthop_group_nexthop_num_func)(
     204             :                                    const struct nexthop_group *nhg))
     205             : {
     206          15 :         if (nhg1 && !nhg2)
     207             :                 return false;
     208             : 
     209          15 :         if (!nhg1 && nhg2)
     210             :                 return false;
     211             : 
     212          15 :         if (nhg1 == nhg2)
     213             :                 return true;
     214             : 
     215          30 :         if (nexthop_group_nexthop_num_func(nhg1)
     216          15 :             != nexthop_group_nexthop_num_func(nhg2))
     217             :                 return false;
     218             : 
     219             :         return true;
     220             : }
     221             : 
     222             : /* This assumes ordered */
     223          15 : bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
     224             :                                     const struct nexthop_group *nhg2)
     225             : {
     226          15 :         struct nexthop *nh1 = NULL;
     227          15 :         struct nexthop *nh2 = NULL;
     228             : 
     229          15 :         if (!nexthop_group_equal_common(nhg1, nhg2,
     230             :                                         &nexthop_group_nexthop_num_no_recurse))
     231             :                 return false;
     232             : 
     233          30 :         for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
     234          15 :              nh1 = nh1->next, nh2 = nh2->next) {
     235          15 :                 if (nh1 && !nh2)
     236             :                         return false;
     237          15 :                 if (!nh1 && nh2)
     238             :                         return false;
     239          15 :                 if (!nexthop_same(nh1, nh2))
     240             :                         return false;
     241             :         }
     242             : 
     243             :         return true;
     244             : }
     245             : 
     246             : /* This assumes ordered */
     247           0 : bool nexthop_group_equal(const struct nexthop_group *nhg1,
     248             :                          const struct nexthop_group *nhg2)
     249             : {
     250           0 :         struct nexthop *nh1 = NULL;
     251           0 :         struct nexthop *nh2 = NULL;
     252             : 
     253           0 :         if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
     254             :                 return false;
     255             : 
     256           0 :         for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
     257           0 :              nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
     258           0 :                 if (nh1 && !nh2)
     259             :                         return false;
     260           0 :                 if (!nh1 && nh2)
     261             :                         return false;
     262           0 :                 if (!nexthop_same(nh1, nh2))
     263             :                         return false;
     264             :         }
     265             : 
     266             :         return true;
     267             : }
     268           2 : struct nexthop_group *nexthop_group_new(void)
     269             : {
     270           2 :         return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
     271             : }
     272             : 
     273         110 : void nexthop_group_copy(struct nexthop_group *to,
     274             :                         const struct nexthop_group *from)
     275             : {
     276         110 :         to->nhgr = from->nhgr;
     277             :         /* Copy everything, including recursive info */
     278         110 :         copy_nexthops(&to->nexthop, from->nexthop, NULL);
     279         110 : }
     280             : 
     281           2 : void nexthop_group_delete(struct nexthop_group **nhg)
     282             : {
     283             :         /* OK to call with NULL group */
     284           2 :         if ((*nhg) == NULL)
     285             :                 return;
     286             : 
     287           2 :         if ((*nhg)->nexthop)
     288           2 :                 nexthops_free((*nhg)->nexthop);
     289             : 
     290           2 :         XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
     291             : }
     292             : 
     293             : /* Add nexthop to the end of a nexthop list.  */
     294         141 : void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
     295             : {
     296         141 :         struct nexthop *last;
     297             : 
     298         141 :         for (last = *target; last && last->next; last = last->next)
     299             :                 ;
     300         141 :         if (last)
     301           0 :                 last->next = nexthop;
     302             :         else
     303         141 :                 *target = nexthop;
     304         141 :         nexthop->prev = last;
     305         141 : }
     306             : 
     307             : /* Add nexthop to sorted list of nexthops */
     308          30 : static void _nexthop_add_sorted(struct nexthop **head,
     309             :                                 struct nexthop *nexthop)
     310             : {
     311          30 :         struct nexthop *position, *prev;
     312             : 
     313          30 :         assert(!nexthop->next);
     314             : 
     315          30 :         for (position = *head, prev = NULL; position;
     316           0 :              prev = position, position = position->next) {
     317           0 :                 if (nexthop_cmp(position, nexthop) > 0) {
     318           0 :                         nexthop->next = position;
     319           0 :                         nexthop->prev = prev;
     320             : 
     321           0 :                         if (nexthop->prev)
     322           0 :                                 nexthop->prev->next = nexthop;
     323             :                         else
     324           0 :                                 *head = nexthop;
     325             : 
     326           0 :                         position->prev = nexthop;
     327           0 :                         return;
     328             :                 }
     329             :         }
     330             : 
     331          30 :         nexthop->prev = prev;
     332          30 :         if (prev)
     333           0 :                 prev->next = nexthop;
     334             :         else
     335          30 :                 *head = nexthop;
     336             : }
     337             : 
     338          30 : void nexthop_group_add_sorted(struct nexthop_group *nhg,
     339             :                               struct nexthop *nexthop)
     340             : {
     341          30 :         struct nexthop *tail;
     342             : 
     343          30 :         assert(!nexthop->next);
     344             : 
     345             :         /* Try to just append to the end first;
     346             :          * trust the list is already sorted
     347             :          */
     348          30 :         tail = nexthop_group_tail(nhg);
     349             : 
     350          30 :         if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
     351           0 :                 tail->next = nexthop;
     352           0 :                 nexthop->prev = tail;
     353             : 
     354           0 :                 return;
     355             :         }
     356             : 
     357          30 :         _nexthop_add_sorted(&nhg->nexthop, nexthop);
     358             : }
     359             : 
     360             : /* Delete nexthop from a nexthop list.  */
     361           0 : void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
     362             : {
     363           0 :         struct nexthop *nexthop;
     364             : 
     365           0 :         for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
     366           0 :                 if (nexthop_same(nh, nexthop))
     367             :                         break;
     368             :         }
     369             : 
     370           0 :         assert(nexthop);
     371             : 
     372           0 :         if (nexthop->prev)
     373           0 :                 nexthop->prev->next = nexthop->next;
     374             :         else
     375           0 :                 nhg->nexthop = nexthop->next;
     376             : 
     377           0 :         if (nexthop->next)
     378           0 :                 nexthop->next->prev = nexthop->prev;
     379             : 
     380           0 :         nh->prev = NULL;
     381           0 :         nh->next = NULL;
     382           0 : }
     383             : 
     384             : /* Unlink a nexthop from the list it's on, unconditionally */
     385           0 : static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
     386             : {
     387             : 
     388           0 :         if (nexthop->prev)
     389           0 :                 nexthop->prev->next = nexthop->next;
     390             :         else {
     391           0 :                 assert(nhg->nexthop == nexthop);
     392           0 :                 assert(nexthop->prev == NULL);
     393           0 :                 nhg->nexthop = nexthop->next;
     394             :         }
     395             : 
     396           0 :         if (nexthop->next)
     397           0 :                 nexthop->next->prev = nexthop->prev;
     398             : 
     399           0 :         nexthop->prev = NULL;
     400           0 :         nexthop->next = NULL;
     401           0 : }
     402             : 
     403             : /*
     404             :  * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
     405             :  */
     406           0 : void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
     407             :                                   const struct nexthop *nh)
     408             : {
     409           0 :         struct nexthop *nexthop, *tail;
     410           0 :         const struct nexthop *nh1;
     411             : 
     412             :         /* We'll try to append to the end of the new list;
     413             :          * if the original list in nh is already sorted, this eliminates
     414             :          * lots of comparison operations.
     415             :          */
     416           0 :         tail = nexthop_group_tail(nhg);
     417             : 
     418           0 :         for (nh1 = nh; nh1; nh1 = nh1->next) {
     419           0 :                 nexthop = nexthop_dup(nh1, NULL);
     420             : 
     421           0 :                 if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
     422           0 :                         tail->next = nexthop;
     423           0 :                         nexthop->prev = tail;
     424             : 
     425           0 :                         tail = nexthop;
     426           0 :                         continue;
     427             :                 }
     428             : 
     429           0 :                 _nexthop_add_sorted(&nhg->nexthop, nexthop);
     430             : 
     431           0 :                 if (tail == NULL)
     432           0 :                         tail = nexthop;
     433             :         }
     434           0 : }
     435             : 
     436             : /* Copy a list of nexthops, no effort made to sort or order them. */
     437         141 : void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
     438             :                    struct nexthop *rparent)
     439             : {
     440         141 :         struct nexthop *nexthop;
     441         141 :         const struct nexthop *nh1;
     442             : 
     443         282 :         for (nh1 = nh; nh1; nh1 = nh1->next) {
     444         141 :                 nexthop = nexthop_dup(nh1, rparent);
     445         141 :                 _nexthop_add(tnh, nexthop);
     446             :         }
     447         141 : }
     448             : 
     449           0 : uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
     450             : {
     451           0 :         struct nexthop *nh;
     452           0 :         uint32_t key = 0;
     453             : 
     454             :         /*
     455             :          * We are not interested in hashing over any recursively
     456             :          * resolved nexthops
     457             :          */
     458           0 :         for (nh = nhg->nexthop; nh; nh = nh->next)
     459           0 :                 key = jhash_1word(nexthop_hash(nh), key);
     460             : 
     461           0 :         return key;
     462             : }
     463             : 
     464         109 : uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
     465             : {
     466         109 :         struct nexthop *nh;
     467         109 :         uint32_t key = 0;
     468             : 
     469         218 :         for (ALL_NEXTHOPS_PTR(nhg, nh))
     470         109 :                 key = jhash_1word(nexthop_hash(nh), key);
     471             : 
     472         109 :         return key;
     473             : }
     474             : 
     475          26 : void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
     476             : {
     477          26 :         struct nexthop *nexthop, *prev;
     478             : 
     479          52 :         for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
     480          26 :                 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
     481          26 :                 for (ALL_NEXTHOPS_PTR(nhg, prev)) {
     482          26 :                         if (prev == nexthop)
     483             :                                 break;
     484           0 :                         if (nexthop_same(nexthop, prev)) {
     485           0 :                                 SET_FLAG(nexthop->flags,
     486             :                                          NEXTHOP_FLAG_DUPLICATE);
     487           0 :                                 break;
     488             :                         }
     489             :                 }
     490             :         }
     491          26 : }
     492             : 
     493           0 : static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
     494             : {
     495           0 :         struct nexthop *nexthop;
     496             : 
     497           0 :         nexthop = nhgc->nhg.nexthop;
     498           0 :         while (nexthop) {
     499           0 :                 struct nexthop *next = nexthop_next(nexthop);
     500             : 
     501           0 :                 _nexthop_del(&nhgc->nhg, nexthop);
     502           0 :                 if (nhg_hooks.del_nexthop)
     503           0 :                         nhg_hooks.del_nexthop(nhgc, nexthop);
     504             : 
     505           0 :                 nexthop_free(nexthop);
     506             : 
     507           0 :                 nexthop = next;
     508             :         }
     509           0 : }
     510             : 
     511           0 : struct nexthop_group_cmd *nhgc_find(const char *name)
     512             : {
     513           0 :         struct nexthop_group_cmd find;
     514             : 
     515           0 :         strlcpy(find.name, name, sizeof(find.name));
     516             : 
     517           0 :         return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
     518             : }
     519             : 
     520           0 : static int nhgc_cmp_helper(const char *a, const char *b)
     521             : {
     522           0 :         if (!a && !b)
     523             :                 return 0;
     524             : 
     525           0 :         if (a && !b)
     526             :                 return -1;
     527             : 
     528           0 :         if (!a && b)
     529             :                 return 1;
     530             : 
     531           0 :         return strcmp(a, b);
     532             : }
     533             : 
     534           0 : static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
     535             : {
     536           0 :         if (!a && !b)
     537             :                 return 0;
     538             : 
     539           0 :         if (a && !b)
     540             :                 return -1;
     541             : 
     542           0 :         if (!a && b)
     543             :                 return 1;
     544             : 
     545           0 :         return sockunion_cmp(a, b);
     546             : }
     547             : 
     548           0 : static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
     549             : {
     550           0 :         int ret;
     551             : 
     552           0 :         ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
     553           0 :         if (ret)
     554             :                 return ret;
     555             : 
     556           0 :         ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
     557           0 :         if (ret)
     558             :                 return ret;
     559             : 
     560           0 :         ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
     561           0 :         if (ret)
     562             :                 return ret;
     563             : 
     564           0 :         ret = ((int)nh2->onlink) - ((int)nh1->onlink);
     565           0 :         if (ret)
     566             :                 return ret;
     567             : 
     568           0 :         return nhgc_cmp_helper(nh1->labels, nh2->labels);
     569             : }
     570             : 
     571           0 : static void nhgl_delete(struct nexthop_hold *nh)
     572             : {
     573           0 :         XFREE(MTYPE_TMP, nh->intf);
     574             : 
     575           0 :         XFREE(MTYPE_TMP, nh->nhvrf_name);
     576             : 
     577           0 :         if (nh->addr)
     578           0 :                 sockunion_free(nh->addr);
     579             : 
     580           0 :         XFREE(MTYPE_TMP, nh->labels);
     581             : 
     582           0 :         XFREE(MTYPE_TMP, nh);
     583           0 : }
     584             : 
     585           0 : static struct nexthop_group_cmd *nhgc_get(const char *name)
     586             : {
     587           0 :         struct nexthop_group_cmd *nhgc;
     588             : 
     589           0 :         nhgc = nhgc_find(name);
     590           0 :         if (!nhgc) {
     591           0 :                 nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
     592           0 :                 strlcpy(nhgc->name, name, sizeof(nhgc->name));
     593             : 
     594           0 :                 QOBJ_REG(nhgc, nexthop_group_cmd);
     595           0 :                 RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
     596             : 
     597           0 :                 nhgc->nhg_list = list_new();
     598           0 :                 nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
     599           0 :                 nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
     600             : 
     601           0 :                 if (nhg_hooks.new)
     602           0 :                         nhg_hooks.new(name);
     603             :         }
     604             : 
     605           0 :         return nhgc;
     606             : }
     607             : 
     608           0 : static void nhgc_delete(struct nexthop_group_cmd *nhgc)
     609             : {
     610           0 :         nhgc_delete_nexthops(nhgc);
     611             : 
     612           0 :         if (nhg_hooks.delete)
     613           0 :                 nhg_hooks.delete(nhgc->name);
     614             : 
     615           0 :         RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
     616             : 
     617           0 :         list_delete(&nhgc->nhg_list);
     618             : 
     619           0 :         QOBJ_UNREG(nhgc);
     620           0 :         XFREE(MTYPE_TMP, nhgc);
     621           0 : }
     622             : 
     623             : DEFINE_QOBJ_TYPE(nexthop_group_cmd);
     624             : 
     625           0 : DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
     626             :            "Enter into the nexthop-group submode\n"
     627             :            "Specify the NAME of the nexthop-group\n")
     628             : {
     629           0 :         const char *nhg_name = argv[1]->arg;
     630           0 :         struct nexthop_group_cmd *nhgc = NULL;
     631             : 
     632           0 :         nhgc = nhgc_get(nhg_name);
     633           0 :         VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
     634             : 
     635           0 :         return CMD_SUCCESS;
     636             : }
     637             : 
     638           0 : DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
     639             :            NO_STR
     640             :            "Delete the nexthop-group\n"
     641             :            "Specify the NAME of the nexthop-group\n")
     642             : {
     643           0 :         const char *nhg_name = argv[2]->arg;
     644           0 :         struct nexthop_group_cmd *nhgc = NULL;
     645             : 
     646           0 :         nhgc = nhgc_find(nhg_name);
     647           0 :         if (nhgc)
     648           0 :                 nhgc_delete(nhgc);
     649             : 
     650           0 :         return CMD_SUCCESS;
     651             : }
     652             : 
     653           0 : DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
     654             :       "backup-group WORD$name",
     655             :       "Specify a group name containing backup nexthops\n"
     656             :       "The name of the backup group\n")
     657             : {
     658           0 :         VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
     659             : 
     660           0 :         strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
     661             : 
     662           0 :         return CMD_SUCCESS;
     663             : }
     664             : 
     665           0 : DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
     666             :       "no backup-group [WORD$name]",
     667             :       NO_STR
     668             :       "Clear group name containing backup nexthops\n"
     669             :       "The name of the backup group\n")
     670             : {
     671           0 :         VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
     672             : 
     673           0 :         nhgc->backup_list_name[0] = 0;
     674             : 
     675           0 :         return CMD_SUCCESS;
     676             : }
     677             : 
     678           0 : DEFPY(nexthop_group_resilience,
     679             :       nexthop_group_resilience_cmd,
     680             :       "resilient buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)",
     681             :       "A resilient Nexthop Group\n"
     682             :       "Buckets in the Hash for this Group\n"
     683             :       "Number of buckets\n"
     684             :       "The Idle timer for this Resilient Nexthop Group in seconds\n"
     685             :       "Number of seconds of Idle time\n"
     686             :       "The length of time that the Nexthop Group can be unbalanced\n"
     687             :       "Number of seconds of Unbalanced time\n")
     688             : {
     689           0 :         VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
     690             : 
     691           0 :         nhgc->nhg.nhgr.buckets = buckets;
     692           0 :         nhgc->nhg.nhgr.idle_timer = idle_timer;
     693           0 :         nhgc->nhg.nhgr.unbalanced_timer = unbalanced_timer;
     694             : 
     695           0 :         if (nhg_hooks.modify)
     696           0 :                 nhg_hooks.modify(nhgc);
     697             : 
     698             :         return CMD_SUCCESS;
     699             : }
     700             : 
     701           0 : DEFPY(no_nexthop_group_resilience,
     702             :       no_nexthop_group_resilience_cmd,
     703             :       "no resilient [buckets (1-256) idle-timer (1-4294967295) unbalanced-timer (1-4294967295)]",
     704             :       NO_STR
     705             :       "A resilient Nexthop Group\n"
     706             :       "Buckets in the Hash for this Group\n"
     707             :       "Number of buckets\n"
     708             :       "The Idle timer for this Resilient Nexthop Group in seconds\n"
     709             :       "Number of seconds of Idle time\n"
     710             :       "The length of time that the Nexthop Group can be unbalanced\n"
     711             :       "Number of seconds of Unbalanced time\n")
     712             : {
     713           0 :         VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
     714             : 
     715           0 :         nhgc->nhg.nhgr.buckets = 0;
     716           0 :         nhgc->nhg.nhgr.idle_timer = 0;
     717           0 :         nhgc->nhg.nhgr.unbalanced_timer = 0;
     718             : 
     719           0 :         return CMD_SUCCESS;
     720             : }
     721             : 
     722           0 : static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
     723             :                                     const char *nhvrf_name,
     724             :                                     const union sockunion *addr,
     725             :                                     const char *intf, bool onlink,
     726             :                                     const char *labels, const uint32_t weight,
     727             :                                     const char *backup_str)
     728             : {
     729           0 :         struct nexthop_hold *nh;
     730             : 
     731           0 :         nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
     732             : 
     733           0 :         if (nhvrf_name)
     734           0 :                 nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
     735           0 :         if (intf)
     736           0 :                 nh->intf = XSTRDUP(MTYPE_TMP, intf);
     737           0 :         if (addr)
     738           0 :                 nh->addr = sockunion_dup(addr);
     739           0 :         if (labels)
     740           0 :                 nh->labels = XSTRDUP(MTYPE_TMP, labels);
     741             : 
     742           0 :         nh->onlink = onlink;
     743             : 
     744           0 :         nh->weight = weight;
     745             : 
     746           0 :         if (backup_str)
     747           0 :                 nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
     748             : 
     749           0 :         listnode_add_sort(nhgc->nhg_list, nh);
     750           0 : }
     751             : 
     752             : /*
     753             :  * Remove config info about a nexthop from group 'nhgc'. Note that we
     754             :  * use only a subset of the available attributes here to determine
     755             :  * a 'match'.
     756             :  * Note that this doesn't change the list of nexthops, only the config
     757             :  * information.
     758             :  */
     759           0 : static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
     760             :                                       const char *nhvrf_name,
     761             :                                       const union sockunion *addr,
     762             :                                       const char *intf)
     763             : {
     764           0 :         struct nexthop_hold *nh;
     765           0 :         struct listnode *node;
     766             : 
     767           0 :         for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
     768           0 :                 if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
     769           0 :                     && nhgc_addr_cmp_helper(addr, nh->addr) == 0
     770           0 :                     && nhgc_cmp_helper(intf, nh->intf) == 0)
     771             :                         break;
     772             :         }
     773             : 
     774             :         /*
     775             :          * Something has gone seriously wrong, fail gracefully
     776             :          */
     777           0 :         if (!nh)
     778             :                 return;
     779             : 
     780           0 :         list_delete_node(nhgc->nhg_list, node);
     781           0 :         nhgl_delete(nh);
     782             : }
     783             : 
     784             : /*
     785             :  * Parse the config strings we support for a single nexthop. This gets used
     786             :  * in a couple of different ways, and we distinguish between transient
     787             :  * failures - such as a still-unprocessed interface - and fatal errors
     788             :  * from label-string parsing.
     789             :  */
     790           0 : static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
     791             :                                         const union sockunion *addr,
     792             :                                         const char *intf, bool onlink,
     793             :                                         const char *name, const char *labels,
     794             :                                         int *lbl_ret, uint32_t weight,
     795             :                                         const char *backup_str)
     796             : {
     797           0 :         int ret = 0;
     798           0 :         struct vrf *vrf;
     799           0 :         int num;
     800             : 
     801           0 :         memset(nhop, 0, sizeof(*nhop));
     802             : 
     803           0 :         if (name)
     804           0 :                 vrf = vrf_lookup_by_name(name);
     805             :         else
     806           0 :                 vrf = vrf_lookup_by_id(VRF_DEFAULT);
     807             : 
     808           0 :         if (!vrf)
     809             :                 return false;
     810             : 
     811           0 :         nhop->vrf_id = vrf->vrf_id;
     812             : 
     813           0 :         if (intf) {
     814           0 :                 nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
     815           0 :                 if (nhop->ifindex == IFINDEX_INTERNAL)
     816             :                         return false;
     817             :         }
     818             : 
     819           0 :         if (onlink)
     820           0 :                 SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
     821             : 
     822           0 :         if (addr) {
     823           0 :                 if (addr->sa.sa_family == AF_INET) {
     824           0 :                         nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
     825           0 :                         if (intf)
     826           0 :                                 nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
     827             :                         else
     828           0 :                                 nhop->type = NEXTHOP_TYPE_IPV4;
     829             :                 } else {
     830           0 :                         nhop->gate.ipv6 = addr->sin6.sin6_addr;
     831           0 :                         if (intf)
     832           0 :                                 nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
     833             :                         else
     834           0 :                                 nhop->type = NEXTHOP_TYPE_IPV6;
     835             :                 }
     836             :         } else
     837           0 :                 nhop->type = NEXTHOP_TYPE_IFINDEX;
     838             : 
     839           0 :         if (labels) {
     840           0 :                 uint8_t num = 0;
     841           0 :                 mpls_label_t larray[MPLS_MAX_LABELS];
     842             : 
     843           0 :                 ret = mpls_str2label(labels, &num, larray);
     844             : 
     845             :                 /* Return label parse result */
     846           0 :                 if (lbl_ret)
     847           0 :                         *lbl_ret = ret;
     848             : 
     849           0 :                 if (ret < 0)
     850           0 :                         return false;
     851           0 :                 else if (num > 0)
     852           0 :                         nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
     853             :                                            num, larray);
     854             :         }
     855             : 
     856           0 :         nhop->weight = weight;
     857             : 
     858           0 :         if (backup_str) {
     859             :                 /* Parse backup indexes */
     860           0 :                 ret = nexthop_str2backups(backup_str,
     861           0 :                                           &num, nhop->backup_idx);
     862           0 :                 if (ret == 0) {
     863           0 :                         SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
     864           0 :                         nhop->backup_num = num;
     865             :                 } else
     866             :                         return false;
     867             :         }
     868             : 
     869             :         return true;
     870             : }
     871             : 
     872             : /*
     873             :  * Wrapper to parse the strings in a 'nexthop_hold'
     874             :  */
     875           0 : static bool nexthop_group_parse_nhh(struct nexthop *nhop,
     876             :                                     const struct nexthop_hold *nhh)
     877             : {
     878           0 :         return (nexthop_group_parse_nexthop(
     879           0 :                 nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
     880           0 :                 nhh->labels, NULL, nhh->weight, nhh->backup_str));
     881             : }
     882             : 
     883           0 : DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
     884             :       "[no] nexthop\
     885             :         <\
     886             :           <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
     887             :           |INTERFACE$intf\
     888             :         >\
     889             :         [{ \
     890             :            nexthop-vrf NAME$vrf_name \
     891             :            |label WORD \
     892             :            |weight (1-255) \
     893             :            |backup-idx WORD \
     894             :         }]",
     895             :       NO_STR
     896             :       "Specify one of the nexthops in this ECMP group\n"
     897             :       "v4 Address\n"
     898             :       "v6 Address\n"
     899             :       "Interface to use\n"
     900             :       "Treat nexthop as directly attached to the interface\n"
     901             :       "Interface to use\n"
     902             :       "If the nexthop is in a different vrf tell us\n"
     903             :       "The nexthop-vrf Name\n"
     904             :       "Specify label(s) for this nexthop\n"
     905             :       "One or more labels in the range (16-1048575) separated by '/'\n"
     906             :       "Weight to be used by the nexthop for purposes of ECMP\n"
     907             :       "Weight value to be used\n"
     908             :       "Specify backup nexthop indexes in another group\n"
     909             :       "One or more indexes in the range (0-254) separated by ','\n")
     910             : {
     911           0 :         VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
     912           0 :         struct nexthop nhop;
     913           0 :         struct nexthop *nh;
     914           0 :         int lbl_ret = 0;
     915           0 :         bool legal;
     916           0 :         int num;
     917           0 :         uint8_t backups[NEXTHOP_MAX_BACKUPS];
     918           0 :         bool yes = !no;
     919             : 
     920             :         /* Pre-parse backup string to validate */
     921           0 :         if (backup_idx) {
     922           0 :                 lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
     923           0 :                 if (lbl_ret < 0) {
     924           0 :                         vty_out(vty, "%% Invalid backups\n");
     925           0 :                         return CMD_WARNING_CONFIG_FAILED;
     926             :                 }
     927             :         }
     928             : 
     929           0 :         legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
     930             :                                             vrf_name, label, &lbl_ret, weight,
     931             :                                             backup_idx);
     932             : 
     933           0 :         if (nhop.type == NEXTHOP_TYPE_IPV6
     934           0 :             && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
     935           0 :                 vty_out(vty,
     936             :                         "Specified a v6 LL with no interface, rejecting\n");
     937           0 :                 return CMD_WARNING_CONFIG_FAILED;
     938             :         }
     939             : 
     940             :         /* Handle label-string errors */
     941           0 :         if (!legal && lbl_ret < 0) {
     942           0 :                 switch (lbl_ret) {
     943           0 :                 case -1:
     944           0 :                         vty_out(vty, "%% Malformed label(s)\n");
     945           0 :                         break;
     946           0 :                 case -2:
     947           0 :                         vty_out(vty,
     948             :                                 "%% Cannot use reserved label(s) (%d-%d)\n",
     949             :                                 MPLS_LABEL_RESERVED_MIN,
     950             :                                 MPLS_LABEL_RESERVED_MAX);
     951           0 :                         break;
     952           0 :                 case -3:
     953           0 :                         vty_out(vty,
     954             :                                 "%% Too many labels. Enter %d or fewer\n",
     955             :                                 MPLS_MAX_LABELS);
     956           0 :                         break;
     957             :                 }
     958           0 :                 return CMD_WARNING_CONFIG_FAILED;
     959             :         }
     960             : 
     961             :         /* Look for an existing nexthop in the config. Note that the test
     962             :          * here tests only some attributes - it's not a complete comparison.
     963             :          * Note that we've got two kinds of objects to manage: 'nexthop_hold'
     964             :          * that represent config that may or may not be valid (yet), and
     965             :          * actual nexthops that have been validated and parsed.
     966             :          */
     967           0 :         nh = nhg_nh_find(&nhgc->nhg, &nhop);
     968             : 
     969             :         /* Always attempt to remove old config info. */
     970           0 :         nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
     971             : 
     972             :         /* Remove any existing nexthop, for delete and replace cases. */
     973           0 :         if (nh) {
     974           0 :                 nexthop_unlink(&nhgc->nhg, nh);
     975             : 
     976           0 :                 if (nhg_hooks.del_nexthop)
     977           0 :                         nhg_hooks.del_nexthop(nhgc, nh);
     978             : 
     979           0 :                 nexthop_free(nh);
     980             :         }
     981           0 :         if (yes) {
     982             :                 /* Add/replace case: capture nexthop if valid, and capture
     983             :                  * config info always.
     984             :                  */
     985           0 :                 if (legal) {
     986           0 :                         nh = nexthop_new();
     987             : 
     988           0 :                         memcpy(nh, &nhop, sizeof(nhop));
     989           0 :                         _nexthop_add(&nhgc->nhg.nexthop, nh);
     990             :                 }
     991             : 
     992             :                 /* Save config always */
     993           0 :                 nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
     994             :                                         label, weight, backup_idx);
     995             : 
     996           0 :                 if (legal && nhg_hooks.add_nexthop)
     997           0 :                         nhg_hooks.add_nexthop(nhgc, nh);
     998             :         }
     999             : 
    1000             :         return CMD_SUCCESS;
    1001             : }
    1002             : 
    1003             : static int nexthop_group_write(struct vty *vty);
    1004             : static struct cmd_node nexthop_group_node = {
    1005             :         .name = "nexthop-group",
    1006             :         .node = NH_GROUP_NODE,
    1007             :         .parent_node = CONFIG_NODE,
    1008             :         .prompt = "%s(config-nh-group)# ",
    1009             :         .config_write = nexthop_group_write,
    1010             : };
    1011             : 
    1012           0 : void nexthop_group_write_nexthop_simple(struct vty *vty,
    1013             :                                         const struct nexthop *nh,
    1014             :                                         char *altifname)
    1015             : {
    1016           0 :         char *ifname;
    1017             : 
    1018           0 :         vty_out(vty, "nexthop ");
    1019             : 
    1020           0 :         if (altifname)
    1021             :                 ifname = altifname;
    1022             :         else
    1023           0 :                 ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
    1024             : 
    1025           0 :         switch (nh->type) {
    1026           0 :         case NEXTHOP_TYPE_IFINDEX:
    1027           0 :                 vty_out(vty, "%s", ifname);
    1028           0 :                 break;
    1029           0 :         case NEXTHOP_TYPE_IPV4:
    1030           0 :                 vty_out(vty, "%pI4", &nh->gate.ipv4);
    1031           0 :                 break;
    1032           0 :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1033           0 :                 vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
    1034           0 :                 break;
    1035           0 :         case NEXTHOP_TYPE_IPV6:
    1036           0 :                 vty_out(vty, "%pI6", &nh->gate.ipv6);
    1037           0 :                 break;
    1038           0 :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1039           0 :                 vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
    1040           0 :                 break;
    1041             :         case NEXTHOP_TYPE_BLACKHOLE:
    1042             :                 break;
    1043             :         }
    1044           0 : }
    1045             : 
    1046           0 : void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
    1047             : {
    1048           0 :         struct vrf *vrf;
    1049           0 :         int i;
    1050             : 
    1051           0 :         nexthop_group_write_nexthop_simple(vty, nh, NULL);
    1052             : 
    1053           0 :         if (nh->vrf_id != VRF_DEFAULT) {
    1054           0 :                 vrf = vrf_lookup_by_id(nh->vrf_id);
    1055           0 :                 vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
    1056             :         }
    1057             : 
    1058           0 :         if (nh->nh_label && nh->nh_label->num_labels > 0) {
    1059           0 :                 char buf[200];
    1060             : 
    1061           0 :                 mpls_label2str(nh->nh_label->num_labels,
    1062           0 :                                nh->nh_label->label,
    1063             :                                buf, sizeof(buf), 0);
    1064           0 :                 vty_out(vty, " label %s", buf);
    1065             :         }
    1066             : 
    1067           0 :         if (nh->weight)
    1068           0 :                 vty_out(vty, " weight %u", nh->weight);
    1069             : 
    1070           0 :         if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
    1071           0 :                 vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
    1072             : 
    1073           0 :                 for (i = 1; i < nh->backup_num; i++)
    1074           0 :                         vty_out(vty, ",%d", nh->backup_idx[i]);
    1075             :         }
    1076             : 
    1077           0 :         vty_out(vty, "\n");
    1078           0 : }
    1079             : 
    1080           0 : void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
    1081             : {
    1082           0 :         struct vrf *vrf;
    1083           0 :         json_object *json_backups = NULL;
    1084           0 :         int i;
    1085             : 
    1086           0 :         switch (nh->type) {
    1087           0 :         case NEXTHOP_TYPE_IFINDEX:
    1088           0 :                 json_object_string_add(j, "nexthop",
    1089           0 :                                        ifindex2ifname(nh->ifindex, nh->vrf_id));
    1090           0 :                 break;
    1091           0 :         case NEXTHOP_TYPE_IPV4:
    1092           0 :                 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
    1093           0 :                 break;
    1094           0 :         case NEXTHOP_TYPE_IPV4_IFINDEX:
    1095           0 :                 json_object_string_addf(j, "nexthop", "%pI4", &nh->gate.ipv4);
    1096           0 :                 json_object_string_add(j, "vrfId",
    1097           0 :                                        ifindex2ifname(nh->ifindex, nh->vrf_id));
    1098           0 :                 break;
    1099           0 :         case NEXTHOP_TYPE_IPV6:
    1100           0 :                 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
    1101           0 :                 break;
    1102           0 :         case NEXTHOP_TYPE_IPV6_IFINDEX:
    1103           0 :                 json_object_string_addf(j, "nexthop", "%pI6", &nh->gate.ipv6);
    1104           0 :                 json_object_string_add(j, "vrfId",
    1105           0 :                                        ifindex2ifname(nh->ifindex, nh->vrf_id));
    1106           0 :                 break;
    1107             :         case NEXTHOP_TYPE_BLACKHOLE:
    1108             :                 break;
    1109             :         }
    1110             : 
    1111           0 :         if (nh->vrf_id != VRF_DEFAULT) {
    1112           0 :                 vrf = vrf_lookup_by_id(nh->vrf_id);
    1113           0 :                 json_object_string_add(j, "targetVrf", vrf->name);
    1114             :         }
    1115             : 
    1116           0 :         if (nh->nh_label && nh->nh_label->num_labels > 0) {
    1117           0 :                 char buf[200];
    1118             : 
    1119           0 :                 mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
    1120             :                                buf, sizeof(buf), 0);
    1121           0 :                 json_object_string_add(j, "label", buf);
    1122             :         }
    1123             : 
    1124           0 :         if (nh->weight)
    1125           0 :                 json_object_int_add(j, "weight", nh->weight);
    1126             : 
    1127           0 :         if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
    1128           0 :                 json_backups = json_object_new_array();
    1129           0 :                 for (i = 0; i < nh->backup_num; i++)
    1130           0 :                         json_object_array_add(
    1131             :                                 json_backups,
    1132           0 :                                 json_object_new_int(nh->backup_idx[i]));
    1133             : 
    1134           0 :                 json_object_object_add(j, "backupIdx", json_backups);
    1135             :         }
    1136           0 : }
    1137             : 
    1138           0 : static void nexthop_group_write_nexthop_internal(struct vty *vty,
    1139             :                                                  const struct nexthop_hold *nh)
    1140             : {
    1141           0 :         vty_out(vty, "nexthop");
    1142             : 
    1143           0 :         if (nh->addr)
    1144           0 :                 vty_out(vty, " %pSU", nh->addr);
    1145             : 
    1146           0 :         if (nh->intf)
    1147           0 :                 vty_out(vty, " %s", nh->intf);
    1148             : 
    1149           0 :         if (nh->onlink)
    1150           0 :                 vty_out(vty, " onlink");
    1151             : 
    1152           0 :         if (nh->nhvrf_name)
    1153           0 :                 vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
    1154             : 
    1155           0 :         if (nh->labels)
    1156           0 :                 vty_out(vty, " label %s", nh->labels);
    1157             : 
    1158           0 :         if (nh->weight)
    1159           0 :                 vty_out(vty, " weight %u", nh->weight);
    1160             : 
    1161           0 :         if (nh->backup_str)
    1162           0 :                 vty_out(vty, " backup-idx %s", nh->backup_str);
    1163             : 
    1164           0 :         vty_out(vty, "\n");
    1165           0 : }
    1166             : 
    1167           0 : static int nexthop_group_write(struct vty *vty)
    1168             : {
    1169           0 :         struct nexthop_group_cmd *nhgc;
    1170           0 :         struct nexthop_hold *nh;
    1171             : 
    1172           0 :         RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
    1173           0 :                 struct listnode *node;
    1174             : 
    1175           0 :                 vty_out(vty, "nexthop-group %s\n", nhgc->name);
    1176             : 
    1177           0 :                 if (nhgc->nhg.nhgr.buckets)
    1178           0 :                         vty_out(vty,
    1179             :                                 " resilient buckets %u idle-timer %u unbalanced-timer %u\n",
    1180             :                                 nhgc->nhg.nhgr.buckets,
    1181             :                                 nhgc->nhg.nhgr.idle_timer,
    1182             :                                 nhgc->nhg.nhgr.unbalanced_timer);
    1183             : 
    1184           0 :                 if (nhgc->backup_list_name[0])
    1185           0 :                         vty_out(vty, " backup-group %s\n",
    1186           0 :                                 nhgc->backup_list_name);
    1187             : 
    1188           0 :                 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
    1189           0 :                         vty_out(vty, " ");
    1190           0 :                         nexthop_group_write_nexthop_internal(vty, nh);
    1191             :                 }
    1192             : 
    1193           0 :                 vty_out(vty, "exit\n");
    1194           0 :                 vty_out(vty, "!\n");
    1195             :         }
    1196             : 
    1197           0 :         return 1;
    1198             : }
    1199             : 
    1200           4 : void nexthop_group_enable_vrf(struct vrf *vrf)
    1201             : {
    1202           4 :         struct nexthop_group_cmd *nhgc;
    1203           4 :         struct nexthop_hold *nhh;
    1204             : 
    1205           8 :         RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
    1206           0 :                 struct listnode *node;
    1207             : 
    1208           0 :                 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
    1209           0 :                         struct nexthop nhop;
    1210           0 :                         struct nexthop *nh;
    1211             : 
    1212           0 :                         if (!nexthop_group_parse_nhh(&nhop, nhh))
    1213           0 :                                 continue;
    1214             : 
    1215           0 :                         nh = nexthop_exists(&nhgc->nhg, &nhop);
    1216             : 
    1217           0 :                         if (nh)
    1218           0 :                                 continue;
    1219             : 
    1220           0 :                         if (nhop.vrf_id != vrf->vrf_id)
    1221           0 :                                 continue;
    1222             : 
    1223           0 :                         nh = nexthop_new();
    1224             : 
    1225           0 :                         memcpy(nh, &nhop, sizeof(nhop));
    1226           0 :                         _nexthop_add(&nhgc->nhg.nexthop, nh);
    1227             : 
    1228           0 :                         if (nhg_hooks.add_nexthop)
    1229           0 :                                 nhg_hooks.add_nexthop(nhgc, nh);
    1230             :                 }
    1231             :         }
    1232           4 : }
    1233             : 
    1234           4 : void nexthop_group_disable_vrf(struct vrf *vrf)
    1235             : {
    1236           4 :         struct nexthop_group_cmd *nhgc;
    1237           4 :         struct nexthop_hold *nhh;
    1238             : 
    1239           8 :         RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
    1240           0 :                 struct listnode *node;
    1241             : 
    1242           0 :                 for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
    1243           0 :                         struct nexthop nhop;
    1244           0 :                         struct nexthop *nh;
    1245             : 
    1246           0 :                         if (!nexthop_group_parse_nhh(&nhop, nhh))
    1247           0 :                                 continue;
    1248             : 
    1249           0 :                         nh = nexthop_exists(&nhgc->nhg, &nhop);
    1250             : 
    1251           0 :                         if (!nh)
    1252           0 :                                 continue;
    1253             : 
    1254           0 :                         if (nh->vrf_id != vrf->vrf_id)
    1255           0 :                                 continue;
    1256             : 
    1257           0 :                         _nexthop_del(&nhgc->nhg, nh);
    1258             : 
    1259           0 :                         if (nhg_hooks.del_nexthop)
    1260           0 :                                 nhg_hooks.del_nexthop(nhgc, nh);
    1261             : 
    1262           0 :                         nexthop_free(nh);
    1263             :                 }
    1264             :         }
    1265           4 : }
    1266             : 
    1267          18 : void nexthop_group_interface_state_change(struct interface *ifp,
    1268             :                                           ifindex_t oldifindex)
    1269             : {
    1270          18 :         struct nexthop_group_cmd *nhgc;
    1271          18 :         struct nexthop_hold *nhh;
    1272             : 
    1273          36 :         RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
    1274           0 :                 struct listnode *node;
    1275           0 :                 struct nexthop *nh;
    1276             : 
    1277           0 :                 if (if_is_up(ifp)) {
    1278           0 :                         for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
    1279           0 :                                 struct nexthop nhop;
    1280             : 
    1281           0 :                                 if (!nexthop_group_parse_nhh(&nhop, nhh))
    1282           0 :                                         continue;
    1283             : 
    1284           0 :                                 switch (nhop.type) {
    1285           0 :                                 case NEXTHOP_TYPE_IPV4:
    1286             :                                 case NEXTHOP_TYPE_IPV6:
    1287             :                                 case NEXTHOP_TYPE_BLACKHOLE:
    1288           0 :                                         continue;
    1289             :                                 case NEXTHOP_TYPE_IFINDEX:
    1290             :                                 case NEXTHOP_TYPE_IPV4_IFINDEX:
    1291             :                                 case NEXTHOP_TYPE_IPV6_IFINDEX:
    1292             :                                         break;
    1293             :                                 }
    1294           0 :                                 nh = nexthop_exists(&nhgc->nhg, &nhop);
    1295             : 
    1296           0 :                                 if (nh)
    1297           0 :                                         continue;
    1298             : 
    1299           0 :                                 if (ifp->ifindex != nhop.ifindex)
    1300           0 :                                         continue;
    1301             : 
    1302           0 :                                 nh = nexthop_new();
    1303             : 
    1304           0 :                                 memcpy(nh, &nhop, sizeof(nhop));
    1305           0 :                                 _nexthop_add(&nhgc->nhg.nexthop, nh);
    1306             : 
    1307           0 :                                 if (nhg_hooks.add_nexthop)
    1308           0 :                                         nhg_hooks.add_nexthop(nhgc, nh);
    1309             :                         }
    1310             :                 } else {
    1311           0 :                         struct nexthop *next_nh;
    1312             : 
    1313           0 :                         for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
    1314           0 :                                 next_nh = nh->next;
    1315           0 :                                 switch (nh->type) {
    1316           0 :                                 case NEXTHOP_TYPE_IPV4:
    1317             :                                 case NEXTHOP_TYPE_IPV6:
    1318             :                                 case NEXTHOP_TYPE_BLACKHOLE:
    1319           0 :                                         continue;
    1320             :                                 case NEXTHOP_TYPE_IFINDEX:
    1321             :                                 case NEXTHOP_TYPE_IPV4_IFINDEX:
    1322             :                                 case NEXTHOP_TYPE_IPV6_IFINDEX:
    1323             :                                         break;
    1324             :                                 }
    1325             : 
    1326           0 :                                 if (oldifindex != nh->ifindex)
    1327           0 :                                         continue;
    1328             : 
    1329           0 :                                 _nexthop_del(&nhgc->nhg, nh);
    1330             : 
    1331           0 :                                 if (nhg_hooks.del_nexthop)
    1332           0 :                                         nhg_hooks.del_nexthop(nhgc, nh);
    1333             : 
    1334           0 :                                 nexthop_free(nh);
    1335             :                         }
    1336             :                 }
    1337             :         }
    1338          18 : }
    1339             : 
    1340           0 : static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
    1341             : {
    1342           0 :         struct nexthop_group_cmd *nhgc;
    1343             : 
    1344           0 :         RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
    1345           0 :                 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
    1346             :         }
    1347           0 : }
    1348             : 
    1349             : static const struct cmd_variable_handler nhg_name_handlers[] = {
    1350             :         {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
    1351             :         {.completions = NULL}};
    1352             : 
    1353           0 : void nexthop_group_init(void (*new)(const char *name),
    1354             :                         void (*modify)(const struct nexthop_group_cmd *nhgc),
    1355             :                         void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
    1356             :                                             const struct nexthop *nhop),
    1357             :                         void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
    1358             :                                             const struct nexthop *nhop),
    1359             :                         void (*delete)(const char *name))
    1360             : {
    1361           0 :         RB_INIT(nhgc_entry_head, &nhgc_entries);
    1362             : 
    1363           0 :         cmd_variable_handler_register(nhg_name_handlers);
    1364             : 
    1365           0 :         install_node(&nexthop_group_node);
    1366           0 :         install_element(CONFIG_NODE, &nexthop_group_cmd);
    1367           0 :         install_element(CONFIG_NODE, &no_nexthop_group_cmd);
    1368             : 
    1369           0 :         install_default(NH_GROUP_NODE);
    1370           0 :         install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
    1371           0 :         install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
    1372           0 :         install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
    1373             : 
    1374           0 :         install_element(NH_GROUP_NODE, &nexthop_group_resilience_cmd);
    1375           0 :         install_element(NH_GROUP_NODE, &no_nexthop_group_resilience_cmd);
    1376             : 
    1377           0 :         memset(&nhg_hooks, 0, sizeof(nhg_hooks));
    1378             : 
    1379           0 :         if (new)
    1380           0 :                 nhg_hooks.new = new;
    1381           0 :         if (modify)
    1382           0 :                 nhg_hooks.modify = modify;
    1383           0 :         if (add_nexthop)
    1384           0 :                 nhg_hooks.add_nexthop = add_nexthop;
    1385           0 :         if (del_nexthop)
    1386           0 :                 nhg_hooks.del_nexthop = del_nexthop;
    1387           0 :         if (delete)
    1388           0 :                 nhg_hooks.delete = delete;
    1389           0 : }

Generated by: LCOV version v1.16-topotato