Annotation of embedaddon/quagga/lib/routemap.c, revision 1.1.1.1

1.1       misho       1: /* Route map function.
                      2:    Copyright (C) 1998, 1999 Kunihiro Ishiguro
                      3: 
                      4: This file is part of GNU Zebra.
                      5: 
                      6: GNU Zebra 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
                      8: Free Software Foundation; either version 2, or (at your option) any
                      9: later version.
                     10: 
                     11: GNU Zebra is distributed in the hope that it will be useful, but
                     12: WITHOUT ANY WARRANTY; without even the implied warranty of
                     13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     14: General Public License for more details.
                     15: 
                     16: You should have received a copy of the GNU General Public License
                     17: along with GNU Zebra; see the file COPYING.  If not, write to the Free
                     18: Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
                     19: 02111-1307, USA.  */
                     20: 
                     21: #include <zebra.h>
                     22: 
                     23: #include "linklist.h"
                     24: #include "memory.h"
                     25: #include "vector.h"
                     26: #include "prefix.h"
                     27: #include "routemap.h"
                     28: #include "command.h"
                     29: #include "vty.h"
                     30: #include "log.h"
                     31: 
                     32: /* Vector for route match rules. */
                     33: static vector route_match_vec;
                     34: 
                     35: /* Vector for route set rules. */
                     36: static vector route_set_vec;
                     37: 
                     38: /* Route map rule. This rule has both `match' rule and `set' rule. */
                     39: struct route_map_rule
                     40: {
                     41:   /* Rule type. */
                     42:   struct route_map_rule_cmd *cmd;
                     43: 
                     44:   /* For pretty printing. */
                     45:   char *rule_str;
                     46: 
                     47:   /* Pre-compiled match rule. */
                     48:   void *value;
                     49: 
                     50:   /* Linked list. */
                     51:   struct route_map_rule *next;
                     52:   struct route_map_rule *prev;
                     53: };
                     54: 
                     55: /* Making route map list. */
                     56: struct route_map_list
                     57: {
                     58:   struct route_map *head;
                     59:   struct route_map *tail;
                     60: 
                     61:   void (*add_hook) (const char *);
                     62:   void (*delete_hook) (const char *);
                     63:   void (*event_hook) (route_map_event_t, const char *); 
                     64: };
                     65: 
                     66: /* Master list of route map. */
                     67: static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL };
                     68: 
                     69: static void
                     70: route_map_rule_delete (struct route_map_rule_list *,
                     71:                       struct route_map_rule *);
                     72: 
                     73: static void
                     74: route_map_index_delete (struct route_map_index *, int);
                     75: 
                     76: /* New route map allocation. Please note route map's name must be
                     77:    specified. */
                     78: static struct route_map *
                     79: route_map_new (const char *name)
                     80: {
                     81:   struct route_map *new;
                     82: 
                     83:   new =  XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map));
                     84:   new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name);
                     85:   return new;
                     86: }
                     87: 
                     88: /* Add new name to route_map. */
                     89: static struct route_map *
                     90: route_map_add (const char *name)
                     91: {
                     92:   struct route_map *map;
                     93:   struct route_map_list *list;
                     94: 
                     95:   map = route_map_new (name);
                     96:   list = &route_map_master;
                     97:     
                     98:   map->next = NULL;
                     99:   map->prev = list->tail;
                    100:   if (list->tail)
                    101:     list->tail->next = map;
                    102:   else
                    103:     list->head = map;
                    104:   list->tail = map;
                    105: 
                    106:   /* Execute hook. */
                    107:   if (route_map_master.add_hook)
                    108:     (*route_map_master.add_hook) (name);
                    109: 
                    110:   return map;
                    111: }
                    112: 
                    113: /* Route map delete from list. */
                    114: static void
                    115: route_map_delete (struct route_map *map)
                    116: {
                    117:   struct route_map_list *list;
                    118:   struct route_map_index *index;
                    119:   char *name;
                    120:   
                    121:   while ((index = map->head) != NULL)
                    122:     route_map_index_delete (index, 0);
                    123: 
                    124:   name = map->name;
                    125: 
                    126:   list = &route_map_master;
                    127: 
                    128:   if (map->next)
                    129:     map->next->prev = map->prev;
                    130:   else
                    131:     list->tail = map->prev;
                    132: 
                    133:   if (map->prev)
                    134:     map->prev->next = map->next;
                    135:   else
                    136:     list->head = map->next;
                    137: 
                    138:   XFREE (MTYPE_ROUTE_MAP, map);
                    139: 
                    140:   /* Execute deletion hook. */
                    141:   if (route_map_master.delete_hook)
                    142:     (*route_map_master.delete_hook) (name);
                    143: 
                    144:   if (name)
                    145:     XFREE (MTYPE_ROUTE_MAP_NAME, name);
                    146: 
                    147: }
                    148: 
                    149: /* Lookup route map by route map name string. */
                    150: struct route_map *
                    151: route_map_lookup_by_name (const char *name)
                    152: {
                    153:   struct route_map *map;
                    154: 
                    155:   for (map = route_map_master.head; map; map = map->next)
                    156:     if (strcmp (map->name, name) == 0)
                    157:       return map;
                    158:   return NULL;
                    159: }
                    160: 
                    161: /* Lookup route map.  If there isn't route map create one and return
                    162:    it. */
                    163: static struct route_map *
                    164: route_map_get (const char *name)
                    165: {
                    166:   struct route_map *map;
                    167: 
                    168:   map = route_map_lookup_by_name (name);
                    169:   if (map == NULL)
                    170:     map = route_map_add (name);
                    171:   return map;
                    172: }
                    173: 
                    174: /* Return route map's type string. */
                    175: static const char *
                    176: route_map_type_str (enum route_map_type type)
                    177: {
                    178:   switch (type)
                    179:     {
                    180:     case RMAP_PERMIT:
                    181:       return "permit";
                    182:       break;
                    183:     case RMAP_DENY:
                    184:       return "deny";
                    185:       break;
                    186:     default:
                    187:       return "";
                    188:       break;
                    189:     }
                    190: }
                    191: 
                    192: static int
                    193: route_map_empty (struct route_map *map)
                    194: {
                    195:   if (map->head == NULL && map->tail == NULL)
                    196:     return 1;
                    197:   else
                    198:     return 0;
                    199: }
                    200: 
                    201: /* show route-map */
                    202: static void
                    203: vty_show_route_map_entry (struct vty *vty, struct route_map *map)
                    204: {
                    205:   struct route_map_index *index;
                    206:   struct route_map_rule *rule;
                    207: 
                    208:   /* Print the name of the protocol */
                    209:   if (zlog_default)
                    210:     vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol],
                    211:              VTY_NEWLINE);
                    212: 
                    213:   for (index = map->head; index; index = index->next)
                    214:     {
                    215:       vty_out (vty, "route-map %s, %s, sequence %d%s",
                    216:                map->name, route_map_type_str (index->type),
                    217:                index->pref, VTY_NEWLINE);
                    218: 
                    219:       /* Description */
                    220:       if (index->description)
                    221:        vty_out (vty, "  Description:%s    %s%s", VTY_NEWLINE,
                    222:                 index->description, VTY_NEWLINE);
                    223:       
                    224:       /* Match clauses */
                    225:       vty_out (vty, "  Match clauses:%s", VTY_NEWLINE);
                    226:       for (rule = index->match_list.head; rule; rule = rule->next)
                    227:         vty_out (vty, "    %s %s%s", 
                    228:                  rule->cmd->str, rule->rule_str, VTY_NEWLINE);
                    229:       
                    230:       vty_out (vty, "  Set clauses:%s", VTY_NEWLINE);
                    231:       for (rule = index->set_list.head; rule; rule = rule->next)
                    232:         vty_out (vty, "    %s %s%s",
                    233:                  rule->cmd->str, rule->rule_str, VTY_NEWLINE);
                    234:       
                    235:       /* Call clause */
                    236:       vty_out (vty, "  Call clause:%s", VTY_NEWLINE);
                    237:       if (index->nextrm)
                    238:         vty_out (vty, "    Call %s%s", index->nextrm, VTY_NEWLINE);
                    239:       
                    240:       /* Exit Policy */
                    241:       vty_out (vty, "  Action:%s", VTY_NEWLINE);
                    242:       if (index->exitpolicy == RMAP_GOTO)
                    243:         vty_out (vty, "    Goto %d%s", index->nextpref, VTY_NEWLINE);
                    244:       else if (index->exitpolicy == RMAP_NEXT)
                    245:         vty_out (vty, "    Continue to next entry%s", VTY_NEWLINE);
                    246:       else if (index->exitpolicy == RMAP_EXIT)
                    247:         vty_out (vty, "    Exit routemap%s", VTY_NEWLINE);
                    248:     }
                    249: }
                    250: 
                    251: static int
                    252: vty_show_route_map (struct vty *vty, const char *name)
                    253: {
                    254:   struct route_map *map;
                    255: 
                    256:   if (name)
                    257:     {
                    258:       map = route_map_lookup_by_name (name);
                    259: 
                    260:       if (map)
                    261:         {
                    262:           vty_show_route_map_entry (vty, map);
                    263:           return CMD_SUCCESS;
                    264:         }
                    265:       else
                    266:         {
                    267:           vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE);
                    268:           return CMD_WARNING;
                    269:         }
                    270:     }
                    271:   else
                    272:     {
                    273:       for (map = route_map_master.head; map; map = map->next)
                    274:        vty_show_route_map_entry (vty, map);
                    275:     }
                    276:   return CMD_SUCCESS;
                    277: }
                    278: 
                    279: 
                    280: /* New route map allocation. Please note route map's name must be
                    281:    specified. */
                    282: static struct route_map_index *
                    283: route_map_index_new (void)
                    284: {
                    285:   struct route_map_index *new;
                    286: 
                    287:   new =  XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index));
                    288:   new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */
                    289:   return new;
                    290: }
                    291: 
                    292: /* Free route map index. */
                    293: static void
                    294: route_map_index_delete (struct route_map_index *index, int notify)
                    295: {
                    296:   struct route_map_rule *rule;
                    297: 
                    298:   /* Free route match. */
                    299:   while ((rule = index->match_list.head) != NULL)
                    300:     route_map_rule_delete (&index->match_list, rule);
                    301: 
                    302:   /* Free route set. */
                    303:   while ((rule = index->set_list.head) != NULL)
                    304:     route_map_rule_delete (&index->set_list, rule);
                    305: 
                    306:   /* Remove index from route map list. */
                    307:   if (index->next)
                    308:     index->next->prev = index->prev;
                    309:   else
                    310:     index->map->tail = index->prev;
                    311: 
                    312:   if (index->prev)
                    313:     index->prev->next = index->next;
                    314:   else
                    315:     index->map->head = index->next;
                    316: 
                    317:   /* Free 'char *nextrm' if not NULL */
                    318:   if (index->nextrm)
                    319:     XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
                    320: 
                    321:     /* Execute event hook. */
                    322:   if (route_map_master.event_hook && notify)
                    323:     (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED,
                    324:                                    index->map->name);
                    325: 
                    326:   XFREE (MTYPE_ROUTE_MAP_INDEX, index);
                    327: }
                    328: 
                    329: /* Lookup index from route map. */
                    330: static struct route_map_index *
                    331: route_map_index_lookup (struct route_map *map, enum route_map_type type,
                    332:                        int pref)
                    333: {
                    334:   struct route_map_index *index;
                    335: 
                    336:   for (index = map->head; index; index = index->next)
                    337:     if ((index->type == type || type == RMAP_ANY)
                    338:        && index->pref == pref)
                    339:       return index;
                    340:   return NULL;
                    341: }
                    342: 
                    343: /* Add new index to route map. */
                    344: static struct route_map_index *
                    345: route_map_index_add (struct route_map *map, enum route_map_type type,
                    346:                     int pref)
                    347: {
                    348:   struct route_map_index *index;
                    349:   struct route_map_index *point;
                    350: 
                    351:   /* Allocate new route map inex. */
                    352:   index = route_map_index_new ();
                    353:   index->map = map;
                    354:   index->type = type;
                    355:   index->pref = pref;
                    356:   
                    357:   /* Compare preference. */
                    358:   for (point = map->head; point; point = point->next)
                    359:     if (point->pref >= pref)
                    360:       break;
                    361: 
                    362:   if (map->head == NULL)
                    363:     {
                    364:       map->head = map->tail = index;
                    365:     }
                    366:   else if (point == NULL)
                    367:     {
                    368:       index->prev = map->tail;
                    369:       map->tail->next = index;
                    370:       map->tail = index;
                    371:     }
                    372:   else if (point == map->head)
                    373:     {
                    374:       index->next = map->head;
                    375:       map->head->prev = index;
                    376:       map->head = index;
                    377:     }
                    378:   else
                    379:     {
                    380:       index->next = point;
                    381:       index->prev = point->prev;
                    382:       if (point->prev)
                    383:        point->prev->next = index;
                    384:       point->prev = index;
                    385:     }
                    386: 
                    387:   /* Execute event hook. */
                    388:   if (route_map_master.event_hook)
                    389:     (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED,
                    390:                                    map->name);
                    391: 
                    392:   return index;
                    393: }
                    394: 
                    395: /* Get route map index. */
                    396: static struct route_map_index *
                    397: route_map_index_get (struct route_map *map, enum route_map_type type, 
                    398:                     int pref)
                    399: {
                    400:   struct route_map_index *index;
                    401: 
                    402:   index = route_map_index_lookup (map, RMAP_ANY, pref);
                    403:   if (index && index->type != type)
                    404:     {
                    405:       /* Delete index from route map. */
                    406:       route_map_index_delete (index, 1);
                    407:       index = NULL;
                    408:     }
                    409:   if (index == NULL)
                    410:     index = route_map_index_add (map, type, pref);
                    411:   return index;
                    412: }
                    413: 
                    414: /* New route map rule */
                    415: static struct route_map_rule *
                    416: route_map_rule_new (void)
                    417: {
                    418:   struct route_map_rule *new;
                    419: 
                    420:   new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
                    421:   return new;
                    422: }
                    423: 
                    424: /* Install rule command to the match list. */
                    425: void
                    426: route_map_install_match (struct route_map_rule_cmd *cmd)
                    427: {
                    428:   vector_set (route_match_vec, cmd);
                    429: }
                    430: 
                    431: /* Install rule command to the set list. */
                    432: void
                    433: route_map_install_set (struct route_map_rule_cmd *cmd)
                    434: {
                    435:   vector_set (route_set_vec, cmd);
                    436: }
                    437: 
                    438: /* Lookup rule command from match list. */
                    439: static struct route_map_rule_cmd *
                    440: route_map_lookup_match (const char *name)
                    441: {
                    442:   unsigned int i;
                    443:   struct route_map_rule_cmd *rule;
                    444: 
                    445:   for (i = 0; i < vector_active (route_match_vec); i++)
                    446:     if ((rule = vector_slot (route_match_vec, i)) != NULL)
                    447:       if (strcmp (rule->str, name) == 0)
                    448:        return rule;
                    449:   return NULL;
                    450: }
                    451: 
                    452: /* Lookup rule command from set list. */
                    453: static struct route_map_rule_cmd *
                    454: route_map_lookup_set (const char *name)
                    455: {
                    456:   unsigned int i;
                    457:   struct route_map_rule_cmd *rule;
                    458: 
                    459:   for (i = 0; i < vector_active (route_set_vec); i++)
                    460:     if ((rule = vector_slot (route_set_vec, i)) != NULL)
                    461:       if (strcmp (rule->str, name) == 0)
                    462:        return rule;
                    463:   return NULL;
                    464: }
                    465: 
                    466: /* Add match and set rule to rule list. */
                    467: static void
                    468: route_map_rule_add (struct route_map_rule_list *list,
                    469:                    struct route_map_rule *rule)
                    470: {
                    471:   rule->next = NULL;
                    472:   rule->prev = list->tail;
                    473:   if (list->tail)
                    474:     list->tail->next = rule;
                    475:   else
                    476:     list->head = rule;
                    477:   list->tail = rule;
                    478: }
                    479: 
                    480: /* Delete rule from rule list. */
                    481: static void
                    482: route_map_rule_delete (struct route_map_rule_list *list,
                    483:                       struct route_map_rule *rule)
                    484: {
                    485:   if (rule->cmd->func_free)
                    486:     (*rule->cmd->func_free) (rule->value);
                    487: 
                    488:   if (rule->rule_str)
                    489:     XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str);
                    490: 
                    491:   if (rule->next)
                    492:     rule->next->prev = rule->prev;
                    493:   else
                    494:     list->tail = rule->prev;
                    495:   if (rule->prev)
                    496:     rule->prev->next = rule->next;
                    497:   else
                    498:     list->head = rule->next;
                    499: 
                    500:   XFREE (MTYPE_ROUTE_MAP_RULE, rule);
                    501: }
                    502: 
                    503: /* strcmp wrapper function which don't crush even argument is NULL. */
                    504: static int
                    505: rulecmp (const char *dst, const char *src)
                    506: {
                    507:   if (dst == NULL)
                    508:     {
                    509:       if (src ==  NULL)
                    510:        return 0;
                    511:       else
                    512:        return 1;
                    513:     }
                    514:   else
                    515:     {
                    516:       if (src == NULL)
                    517:        return 1;
                    518:       else
                    519:        return strcmp (dst, src);
                    520:     }
                    521:   return 1;
                    522: }
                    523: 
                    524: /* Add match statement to route map. */
                    525: int
                    526: route_map_add_match (struct route_map_index *index, const char *match_name,
                    527:                      const char *match_arg)
                    528: {
                    529:   struct route_map_rule *rule;
                    530:   struct route_map_rule *next;
                    531:   struct route_map_rule_cmd *cmd;
                    532:   void *compile;
                    533:   int replaced = 0;
                    534: 
                    535:   /* First lookup rule for add match statement. */
                    536:   cmd = route_map_lookup_match (match_name);
                    537:   if (cmd == NULL)
                    538:     return RMAP_RULE_MISSING;
                    539: 
                    540:   /* Next call compile function for this match statement. */
                    541:   if (cmd->func_compile)
                    542:     {
                    543:       compile= (*cmd->func_compile)(match_arg);
                    544:       if (compile == NULL)
                    545:        return RMAP_COMPILE_ERROR;
                    546:     }
                    547:   else
                    548:     compile = NULL;
                    549: 
                    550:   /* If argument is completely same ignore it. */
                    551:   for (rule = index->match_list.head; rule; rule = next)
                    552:     {
                    553:       next = rule->next;
                    554:       if (rule->cmd == cmd)
                    555:        {       
                    556:          route_map_rule_delete (&index->match_list, rule);
                    557:          replaced = 1;
                    558:        }
                    559:     }
                    560: 
                    561:   /* Add new route map match rule. */
                    562:   rule = route_map_rule_new ();
                    563:   rule->cmd = cmd;
                    564:   rule->value = compile;
                    565:   if (match_arg)
                    566:     rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg);
                    567:   else
                    568:     rule->rule_str = NULL;
                    569: 
                    570:   /* Add new route match rule to linked list. */
                    571:   route_map_rule_add (&index->match_list, rule);
                    572: 
                    573:   /* Execute event hook. */
                    574:   if (route_map_master.event_hook)
                    575:     (*route_map_master.event_hook) (replaced ?
                    576:                                    RMAP_EVENT_MATCH_REPLACED:
                    577:                                    RMAP_EVENT_MATCH_ADDED,
                    578:                                    index->map->name);
                    579: 
                    580:   return 0;
                    581: }
                    582: 
                    583: /* Delete specified route match rule. */
                    584: int
                    585: route_map_delete_match (struct route_map_index *index, const char *match_name,
                    586:                         const char *match_arg)
                    587: {
                    588:   struct route_map_rule *rule;
                    589:   struct route_map_rule_cmd *cmd;
                    590: 
                    591:   cmd = route_map_lookup_match (match_name);
                    592:   if (cmd == NULL)
                    593:     return 1;
                    594:   
                    595:   for (rule = index->match_list.head; rule; rule = rule->next)
                    596:     if (rule->cmd == cmd && 
                    597:        (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
                    598:       {
                    599:        route_map_rule_delete (&index->match_list, rule);
                    600:        /* Execute event hook. */
                    601:        if (route_map_master.event_hook)
                    602:          (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED,
                    603:                                          index->map->name);
                    604:        return 0;
                    605:       }
                    606:   /* Can't find matched rule. */
                    607:   return 1;
                    608: }
                    609: 
                    610: /* Add route-map set statement to the route map. */
                    611: int
                    612: route_map_add_set (struct route_map_index *index, const char *set_name,
                    613:                    const char *set_arg)
                    614: {
                    615:   struct route_map_rule *rule;
                    616:   struct route_map_rule *next;
                    617:   struct route_map_rule_cmd *cmd;
                    618:   void *compile;
                    619:   int replaced = 0;
                    620: 
                    621:   cmd = route_map_lookup_set (set_name);
                    622:   if (cmd == NULL)
                    623:     return RMAP_RULE_MISSING;
                    624: 
                    625:   /* Next call compile function for this match statement. */
                    626:   if (cmd->func_compile)
                    627:     {
                    628:       compile= (*cmd->func_compile)(set_arg);
                    629:       if (compile == NULL)
                    630:        return RMAP_COMPILE_ERROR;
                    631:     }
                    632:   else
                    633:     compile = NULL;
                    634: 
                    635:  /* Add by WJL. if old set command of same kind exist, delete it first
                    636:     to ensure only one set command of same kind exist under a
                    637:     route_map_index. */
                    638:   for (rule = index->set_list.head; rule; rule = next)
                    639:     {
                    640:       next = rule->next;
                    641:       if (rule->cmd == cmd)
                    642:        {
                    643:          route_map_rule_delete (&index->set_list, rule);
                    644:          replaced = 1;
                    645:        }
                    646:     }
                    647: 
                    648:   /* Add new route map match rule. */
                    649:   rule = route_map_rule_new ();
                    650:   rule->cmd = cmd;
                    651:   rule->value = compile;
                    652:   if (set_arg)
                    653:     rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg);
                    654:   else
                    655:     rule->rule_str = NULL;
                    656: 
                    657:   /* Add new route match rule to linked list. */
                    658:   route_map_rule_add (&index->set_list, rule);
                    659: 
                    660:   /* Execute event hook. */
                    661:   if (route_map_master.event_hook)
                    662:     (*route_map_master.event_hook) (replaced ?
                    663:                                    RMAP_EVENT_SET_REPLACED:
                    664:                                    RMAP_EVENT_SET_ADDED,
                    665:                                    index->map->name);
                    666:   return 0;
                    667: }
                    668: 
                    669: /* Delete route map set rule. */
                    670: int
                    671: route_map_delete_set (struct route_map_index *index, const char *set_name,
                    672:                       const char *set_arg)
                    673: {
                    674:   struct route_map_rule *rule;
                    675:   struct route_map_rule_cmd *cmd;
                    676: 
                    677:   cmd = route_map_lookup_set (set_name);
                    678:   if (cmd == NULL)
                    679:     return 1;
                    680:   
                    681:   for (rule = index->set_list.head; rule; rule = rule->next)
                    682:     if ((rule->cmd == cmd) &&
                    683:          (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
                    684:       {
                    685:         route_map_rule_delete (&index->set_list, rule);
                    686:        /* Execute event hook. */
                    687:        if (route_map_master.event_hook)
                    688:          (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED,
                    689:                                          index->map->name);
                    690:         return 0;
                    691:       }
                    692:   /* Can't find matched rule. */
                    693:   return 1;
                    694: }
                    695: 
                    696: /* Apply route map's each index to the object.
                    697: 
                    698:    The matrix for a route-map looks like this:
                    699:    (note, this includes the description for the "NEXT"
                    700:    and "GOTO" frobs now
                    701:   
                    702:               Match   |   No Match
                    703:                       |
                    704:     permit    action  |     cont
                    705:                       |
                    706:     ------------------+---------------
                    707:                       |
                    708:     deny      deny    |     cont
                    709:                       |
                    710:   
                    711:    action)
                    712:       -Apply Set statements, accept route
                    713:       -If Call statement is present jump to the specified route-map, if it
                    714:          denies the route we finish.
                    715:       -If NEXT is specified, goto NEXT statement
                    716:       -If GOTO is specified, goto the first clause where pref > nextpref
                    717:       -If nothing is specified, do as Cisco and finish
                    718:    deny)
                    719:       -Route is denied by route-map.
                    720:    cont)
                    721:       -Goto Next index
                    722:   
                    723:    If we get no matches after we've processed all updates, then the route
                    724:    is dropped too.
                    725:   
                    726:    Some notes on the new "CALL", "NEXT" and "GOTO"
                    727:      call WORD        - If this clause is matched, then the set statements
                    728:                         are executed and then we jump to route-map 'WORD'. If
                    729:                         this route-map denies the route, we finish, in other case we
                    730:                         do whatever the exit policy (EXIT, NEXT or GOTO) tells.
                    731:      on-match next    - If this clause is matched, then the set statements
                    732:                         are executed and then we drop through to the next clause
                    733:      on-match goto n  - If this clause is matched, then the set statments
                    734:                         are executed and then we goto the nth clause, or the
                    735:                         first clause greater than this. In order to ensure
                    736:                         route-maps *always* exit, you cannot jump backwards.
                    737:                         Sorry ;)
                    738:   
                    739:    We need to make sure our route-map processing matches the above
                    740: */
                    741: 
                    742: static route_map_result_t
                    743: route_map_apply_match (struct route_map_rule_list *match_list,
                    744:                        struct prefix *prefix, route_map_object_t type,
                    745:                        void *object)
                    746: {
                    747:   route_map_result_t ret = RMAP_NOMATCH;
                    748:   struct route_map_rule *match;
                    749: 
                    750: 
                    751:   /* Check all match rule and if there is no match rule, go to the
                    752:      set statement. */
                    753:   if (!match_list->head)
                    754:     ret = RMAP_MATCH;
                    755:   else
                    756:     {
                    757:       for (match = match_list->head; match; match = match->next)
                    758:         {
                    759:           /* Try each match statement in turn, If any do not return
                    760:              RMAP_MATCH, return, otherwise continue on to next match 
                    761:              statement. All match statements must match for end-result
                    762:              to be a match. */
                    763:           ret = (*match->cmd->func_apply) (match->value, prefix,
                    764:                                            type, object);
                    765:           if (ret != RMAP_MATCH)
                    766:             return ret;
                    767:         }
                    768:     }
                    769:   return ret;
                    770: }
                    771: 
                    772: /* Apply route map to the object. */
                    773: route_map_result_t
                    774: route_map_apply (struct route_map *map, struct prefix *prefix,
                    775:                  route_map_object_t type, void *object)
                    776: {
                    777:   static int recursion = 0;
                    778:   int ret = 0;
                    779:   struct route_map_index *index;
                    780:   struct route_map_rule *set;
                    781: 
                    782:   if (recursion > RMAP_RECURSION_LIMIT)
                    783:     {
                    784:       zlog (NULL, LOG_WARNING,
                    785:             "route-map recursion limit (%d) reached, discarding route",
                    786:             RMAP_RECURSION_LIMIT);
                    787:       recursion = 0;
                    788:       return RMAP_DENYMATCH;
                    789:     }
                    790: 
                    791:   if (map == NULL)
                    792:     return RMAP_DENYMATCH;
                    793: 
                    794:   for (index = map->head; index; index = index->next)
                    795:     {
                    796:       /* Apply this index. */
                    797:       ret = route_map_apply_match (&index->match_list, prefix, type, object);
                    798: 
                    799:       /* Now we apply the matrix from above */
                    800:       if (ret == RMAP_NOMATCH)
                    801:         /* 'cont' from matrix - continue to next route-map sequence */
                    802:         continue;
                    803:       else if (ret == RMAP_MATCH)
                    804:         {
                    805:           if (index->type == RMAP_PERMIT)
                    806:             /* 'action' */
                    807:             {
                    808:               /* permit+match must execute sets */
                    809:               for (set = index->set_list.head; set; set = set->next)
                    810:                 ret = (*set->cmd->func_apply) (set->value, prefix,
                    811:                                                type, object);
                    812: 
                    813:               /* Call another route-map if available */
                    814:               if (index->nextrm)
                    815:                 {
                    816:                   struct route_map *nextrm =
                    817:                                     route_map_lookup_by_name (index->nextrm);
                    818: 
                    819:                   if (nextrm) /* Target route-map found, jump to it */
                    820:                     {
                    821:                       recursion++;
                    822:                       ret = route_map_apply (nextrm, prefix, type, object);
                    823:                       recursion--;
                    824:                     }
                    825: 
                    826:                   /* If nextrm returned 'deny', finish. */
                    827:                   if (ret == RMAP_DENYMATCH)
                    828:                     return ret;
                    829:                 }
                    830:                 
                    831:               switch (index->exitpolicy)
                    832:                 {
                    833:                   case RMAP_EXIT:
                    834:                     return ret;
                    835:                   case RMAP_NEXT:
                    836:                     continue;
                    837:                   case RMAP_GOTO:
                    838:                     {
                    839:                       /* Find the next clause to jump to */
                    840:                       struct route_map_index *next = index->next;
                    841:                       int nextpref = index->nextpref;
                    842: 
                    843:                       while (next && next->pref < nextpref)
                    844:                         {
                    845:                           index = next;
                    846:                           next = next->next;
                    847:                         }
                    848:                       if (next == NULL)
                    849:                         {
                    850:                           /* No clauses match! */
                    851:                           return ret;
                    852:                         }
                    853:                     }
                    854:                 }
                    855:             }
                    856:           else if (index->type == RMAP_DENY)
                    857:             /* 'deny' */
                    858:             {
                    859:                 return RMAP_DENYMATCH;
                    860:             }
                    861:         }
                    862:     }
                    863:   /* Finally route-map does not match at all. */
                    864:   return RMAP_DENYMATCH;
                    865: }
                    866: 
                    867: void
                    868: route_map_add_hook (void (*func) (const char *))
                    869: {
                    870:   route_map_master.add_hook = func;
                    871: }
                    872: 
                    873: void
                    874: route_map_delete_hook (void (*func) (const char *))
                    875: {
                    876:   route_map_master.delete_hook = func;
                    877: }
                    878: 
                    879: void
                    880: route_map_event_hook (void (*func) (route_map_event_t, const char *))
                    881: {
                    882:   route_map_master.event_hook = func;
                    883: }
                    884: 
                    885: void
                    886: route_map_init (void)
                    887: {
                    888:   /* Make vector for match and set. */
                    889:   route_match_vec = vector_init (1);
                    890:   route_set_vec = vector_init (1);
                    891: }
                    892: 
                    893: void
                    894: route_map_finish (void)
                    895: {
                    896:   vector_free (route_match_vec);
                    897:   route_match_vec = NULL;
                    898:   vector_free (route_set_vec);
                    899:   route_set_vec = NULL;
                    900: }
                    901: 
                    902: /* VTY related functions. */
                    903: DEFUN (route_map,
                    904:        route_map_cmd,
                    905:        "route-map WORD (deny|permit) <1-65535>",
                    906:        "Create route-map or enter route-map command mode\n"
                    907:        "Route map tag\n"
                    908:        "Route map denies set operations\n"
                    909:        "Route map permits set operations\n"
                    910:        "Sequence to insert to/delete from existing route-map entry\n")
                    911: {
                    912:   int permit;
                    913:   unsigned long pref;
                    914:   struct route_map *map;
                    915:   struct route_map_index *index;
                    916:   char *endptr = NULL;
                    917: 
                    918:   /* Permit check. */
                    919:   if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
                    920:     permit = RMAP_PERMIT;
                    921:   else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
                    922:     permit = RMAP_DENY;
                    923:   else
                    924:     {
                    925:       vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
                    926:       return CMD_WARNING;
                    927:     }
                    928: 
                    929:   /* Preference check. */
                    930:   pref = strtoul (argv[2], &endptr, 10);
                    931:   if (pref == ULONG_MAX || *endptr != '\0')
                    932:     {
                    933:       vty_out (vty, "the fourth field must be positive integer%s",
                    934:               VTY_NEWLINE);
                    935:       return CMD_WARNING;
                    936:     }
                    937:   if (pref == 0 || pref > 65535)
                    938:     {
                    939:       vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
                    940:       return CMD_WARNING;
                    941:     }
                    942: 
                    943:   /* Get route map. */
                    944:   map = route_map_get (argv[0]);
                    945:   index = route_map_index_get (map, permit, pref);
                    946: 
                    947:   vty->index = index;
                    948:   vty->node = RMAP_NODE;
                    949:   return CMD_SUCCESS;
                    950: }
                    951: 
                    952: DEFUN (no_route_map_all,
                    953:        no_route_map_all_cmd,
                    954:        "no route-map WORD",
                    955:        NO_STR
                    956:        "Create route-map or enter route-map command mode\n"
                    957:        "Route map tag\n")
                    958: {
                    959:   struct route_map *map;
                    960: 
                    961:   map = route_map_lookup_by_name (argv[0]);
                    962:   if (map == NULL)
                    963:     {
                    964:       vty_out (vty, "%% Could not find route-map %s%s",
                    965:               argv[0], VTY_NEWLINE);
                    966:       return CMD_WARNING;
                    967:     }
                    968: 
                    969:   route_map_delete (map);
                    970: 
                    971:   return CMD_SUCCESS;
                    972: }
                    973: 
                    974: DEFUN (no_route_map,
                    975:        no_route_map_cmd,
                    976:        "no route-map WORD (deny|permit) <1-65535>",
                    977:        NO_STR
                    978:        "Create route-map or enter route-map command mode\n"
                    979:        "Route map tag\n"
                    980:        "Route map denies set operations\n"
                    981:        "Route map permits set operations\n"
                    982:        "Sequence to insert to/delete from existing route-map entry\n")
                    983: {
                    984:   int permit;
                    985:   unsigned long pref;
                    986:   struct route_map *map;
                    987:   struct route_map_index *index;
                    988:   char *endptr = NULL;
                    989: 
                    990:   /* Permit check. */
                    991:   if (strncmp (argv[1], "permit", strlen (argv[1])) == 0)
                    992:     permit = RMAP_PERMIT;
                    993:   else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0)
                    994:     permit = RMAP_DENY;
                    995:   else
                    996:     {
                    997:       vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE);
                    998:       return CMD_WARNING;
                    999:     }
                   1000: 
                   1001:   /* Preference. */
                   1002:   pref = strtoul (argv[2], &endptr, 10);
                   1003:   if (pref == ULONG_MAX || *endptr != '\0')
                   1004:     {
                   1005:       vty_out (vty, "the fourth field must be positive integer%s",
                   1006:               VTY_NEWLINE);
                   1007:       return CMD_WARNING;
                   1008:     }
                   1009:   if (pref == 0 || pref > 65535)
                   1010:     {
                   1011:       vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE);
                   1012:       return CMD_WARNING;
                   1013:     }
                   1014: 
                   1015:   /* Existence check. */
                   1016:   map = route_map_lookup_by_name (argv[0]);
                   1017:   if (map == NULL)
                   1018:     {
                   1019:       vty_out (vty, "%% Could not find route-map %s%s",
                   1020:               argv[0], VTY_NEWLINE);
                   1021:       return CMD_WARNING;
                   1022:     }
                   1023: 
                   1024:   /* Lookup route map index. */
                   1025:   index = route_map_index_lookup (map, permit, pref);
                   1026:   if (index == NULL)
                   1027:     {
                   1028:       vty_out (vty, "%% Could not find route-map entry %s %s%s", 
                   1029:               argv[0], argv[2], VTY_NEWLINE);
                   1030:       return CMD_WARNING;
                   1031:     }
                   1032: 
                   1033:   /* Delete index from route map. */
                   1034:   route_map_index_delete (index, 1);
                   1035: 
                   1036:   /* If this route rule is the last one, delete route map itself. */
                   1037:   if (route_map_empty (map))
                   1038:     route_map_delete (map);
                   1039: 
                   1040:   return CMD_SUCCESS;
                   1041: }
                   1042: 
                   1043: DEFUN (rmap_onmatch_next,
                   1044:        rmap_onmatch_next_cmd,
                   1045:        "on-match next",
                   1046:        "Exit policy on matches\n"
                   1047:        "Next clause\n")
                   1048: {
                   1049:   struct route_map_index *index;
                   1050: 
                   1051:   index = vty->index;
                   1052: 
                   1053:   if (index)
                   1054:     index->exitpolicy = RMAP_NEXT;
                   1055: 
                   1056:   return CMD_SUCCESS;
                   1057: }
                   1058: 
                   1059: DEFUN (no_rmap_onmatch_next,
                   1060:        no_rmap_onmatch_next_cmd,
                   1061:        "no on-match next",
                   1062:        NO_STR
                   1063:        "Exit policy on matches\n"
                   1064:        "Next clause\n")
                   1065: {
                   1066:   struct route_map_index *index;
                   1067: 
                   1068:   index = vty->index;
                   1069:   
                   1070:   if (index)
                   1071:     index->exitpolicy = RMAP_EXIT;
                   1072: 
                   1073:   return CMD_SUCCESS;
                   1074: }
                   1075: 
                   1076: DEFUN (rmap_onmatch_goto,
                   1077:        rmap_onmatch_goto_cmd,
                   1078:        "on-match goto <1-65535>",
                   1079:        "Exit policy on matches\n"
                   1080:        "Goto Clause number\n"
                   1081:        "Number\n")
                   1082: {
                   1083:   struct route_map_index *index = vty->index;
                   1084:   int d = 0;
                   1085: 
                   1086:   if (index)
                   1087:     {
                   1088:       if (argc == 1 && argv[0])
                   1089:         VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
                   1090:       else
                   1091:         d = index->pref + 1;
                   1092:       
                   1093:       if (d <= index->pref)
                   1094:        {
                   1095:          /* Can't allow you to do that, Dave */
                   1096:          vty_out (vty, "can't jump backwards in route-maps%s", 
                   1097:                   VTY_NEWLINE);
                   1098:          return CMD_WARNING;
                   1099:        }
                   1100:       else
                   1101:        {
                   1102:          index->exitpolicy = RMAP_GOTO;
                   1103:          index->nextpref = d;
                   1104:        }
                   1105:     }
                   1106:   return CMD_SUCCESS;
                   1107: }
                   1108: 
                   1109: DEFUN (no_rmap_onmatch_goto,
                   1110:        no_rmap_onmatch_goto_cmd,
                   1111:        "no on-match goto",
                   1112:        NO_STR
                   1113:        "Exit policy on matches\n"
                   1114:        "Goto Clause number\n")
                   1115: {
                   1116:   struct route_map_index *index;
                   1117: 
                   1118:   index = vty->index;
                   1119: 
                   1120:   if (index)
                   1121:     index->exitpolicy = RMAP_EXIT;
                   1122:   
                   1123:   return CMD_SUCCESS;
                   1124: }
                   1125: 
                   1126: /* Cisco/GNU Zebra compatible ALIASes for on-match next */
                   1127: ALIAS (rmap_onmatch_goto,
                   1128:        rmap_continue_cmd,
                   1129:        "continue",
                   1130:        "Continue on a different entry within the route-map\n")
                   1131: 
                   1132: ALIAS (no_rmap_onmatch_goto,
                   1133:        no_rmap_continue_cmd,
                   1134:        "no continue",
                   1135:        NO_STR
                   1136:        "Continue on a different entry within the route-map\n")
                   1137: 
                   1138: /* GNU Zebra compatible */
                   1139: ALIAS (rmap_onmatch_goto,
                   1140:        rmap_continue_seq_cmd,
                   1141:        "continue <1-65535>",
                   1142:        "Continue on a different entry within the route-map\n"
                   1143:        "Route-map entry sequence number\n")
                   1144: 
                   1145: ALIAS (no_rmap_onmatch_goto,
                   1146:        no_rmap_continue_seq,
                   1147:        "no continue <1-65535>",
                   1148:        NO_STR
                   1149:        "Continue on a different entry within the route-map\n"
                   1150:        "Route-map entry sequence number\n")
                   1151: 
                   1152: DEFUN (rmap_show_name,
                   1153:        rmap_show_name_cmd,
                   1154:        "show route-map [WORD]",
                   1155:        SHOW_STR
                   1156:        "route-map information\n"
                   1157:        "route-map name\n")
                   1158: {
                   1159:     const char *name = NULL;
                   1160:     if (argc)
                   1161:       name = argv[0];
                   1162:     return vty_show_route_map (vty, name);
                   1163: }
                   1164: 
                   1165: ALIAS (rmap_onmatch_goto,
                   1166:       rmap_continue_index_cmd,
                   1167:       "continue <1-65536>",
                   1168:       "Exit policy on matches\n"
                   1169:       "Goto Clause number\n")
                   1170: 
                   1171: DEFUN (rmap_call,
                   1172:        rmap_call_cmd,
                   1173:        "call WORD",
                   1174:        "Jump to another Route-Map after match+set\n"
                   1175:        "Target route-map name\n")
                   1176: {
                   1177:   struct route_map_index *index;
                   1178: 
                   1179:   index = vty->index;
                   1180:   if (index)
                   1181:     {
                   1182:       if (index->nextrm)
                   1183:           XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
                   1184:       index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]);
                   1185:     }
                   1186:   return CMD_SUCCESS;
                   1187: }
                   1188: 
                   1189: DEFUN (no_rmap_call,
                   1190:        no_rmap_call_cmd,
                   1191:        "no call",
                   1192:        NO_STR
                   1193:        "Jump to another Route-Map after match+set\n")
                   1194: {
                   1195:   struct route_map_index *index;
                   1196: 
                   1197:   index = vty->index;
                   1198: 
                   1199:   if (index->nextrm)
                   1200:     {
                   1201:       XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm);
                   1202:       index->nextrm = NULL;
                   1203:     }
                   1204: 
                   1205:   return CMD_SUCCESS;
                   1206: }
                   1207: 
                   1208: DEFUN (rmap_description,
                   1209:        rmap_description_cmd,
                   1210:        "description .LINE",
                   1211:        "Route-map comment\n"
                   1212:        "Comment describing this route-map rule\n")
                   1213: {
                   1214:   struct route_map_index *index;
                   1215: 
                   1216:   index = vty->index;
                   1217:   if (index)
                   1218:     {
                   1219:       if (index->description)
                   1220:        XFREE (MTYPE_TMP, index->description);
                   1221:       index->description = argv_concat (argv, argc, 0);
                   1222:     }
                   1223:   return CMD_SUCCESS;
                   1224: }
                   1225: 
                   1226: DEFUN (no_rmap_description,
                   1227:        no_rmap_description_cmd,
                   1228:        "no description",
                   1229:        NO_STR
                   1230:        "Route-map comment\n")
                   1231: {
                   1232:   struct route_map_index *index;
                   1233: 
                   1234:   index = vty->index;
                   1235:   if (index)
                   1236:     {
                   1237:       if (index->description)
                   1238:        XFREE (MTYPE_TMP, index->description);
                   1239:       index->description = NULL;
                   1240:     }
                   1241:   return CMD_SUCCESS;
                   1242: }
                   1243: 
                   1244: /* Configuration write function. */
                   1245: static int
                   1246: route_map_config_write (struct vty *vty)
                   1247: {
                   1248:   struct route_map *map;
                   1249:   struct route_map_index *index;
                   1250:   struct route_map_rule *rule;
                   1251:   int first = 1;
                   1252:   int write = 0;
                   1253: 
                   1254:   for (map = route_map_master.head; map; map = map->next)
                   1255:     for (index = map->head; index; index = index->next)
                   1256:       {
                   1257:        if (!first)
                   1258:          vty_out (vty, "!%s", VTY_NEWLINE);
                   1259:        else
                   1260:          first = 0;
                   1261: 
                   1262:        vty_out (vty, "route-map %s %s %d%s", 
                   1263:                 map->name,
                   1264:                 route_map_type_str (index->type),
                   1265:                 index->pref, VTY_NEWLINE);
                   1266: 
                   1267:        if (index->description)
                   1268:          vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
                   1269: 
                   1270:        for (rule = index->match_list.head; rule; rule = rule->next)
                   1271:          vty_out (vty, " match %s %s%s", rule->cmd->str, 
                   1272:                   rule->rule_str ? rule->rule_str : "",
                   1273:                   VTY_NEWLINE);
                   1274: 
                   1275:        for (rule = index->set_list.head; rule; rule = rule->next)
                   1276:          vty_out (vty, " set %s %s%s", rule->cmd->str,
                   1277:                   rule->rule_str ? rule->rule_str : "",
                   1278:                   VTY_NEWLINE);
                   1279:    if (index->nextrm)
                   1280:      vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE);
                   1281:        if (index->exitpolicy == RMAP_GOTO)
                   1282:       vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
                   1283:        if (index->exitpolicy == RMAP_NEXT)
                   1284:          vty_out (vty," on-match next%s", VTY_NEWLINE);
                   1285:        
                   1286:        write++;
                   1287:       }
                   1288:   return write;
                   1289: }
                   1290: 
                   1291: /* Route map node structure. */
                   1292: static struct cmd_node rmap_node =
                   1293: {
                   1294:   RMAP_NODE,
                   1295:   "%s(config-route-map)# ",
                   1296:   1
                   1297: };
                   1298: 
                   1299: /* Initialization of route map vector. */
                   1300: void
                   1301: route_map_init_vty (void)
                   1302: {
                   1303:   /* Install route map top node. */
                   1304:   install_node (&rmap_node, route_map_config_write);
                   1305: 
                   1306:   /* Install route map commands. */
                   1307:   install_default (RMAP_NODE);
                   1308:   install_element (CONFIG_NODE, &route_map_cmd);
                   1309:   install_element (CONFIG_NODE, &no_route_map_cmd);
                   1310:   install_element (CONFIG_NODE, &no_route_map_all_cmd);
                   1311: 
                   1312:   /* Install the on-match stuff */
                   1313:   install_element (RMAP_NODE, &route_map_cmd);
                   1314:   install_element (RMAP_NODE, &rmap_onmatch_next_cmd);
                   1315:   install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
                   1316:   install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
                   1317:   install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
                   1318:   
                   1319:   /* Install the continue stuff (ALIAS of on-match). */
                   1320:   install_element (RMAP_NODE, &rmap_continue_cmd);
                   1321:   install_element (RMAP_NODE, &no_rmap_continue_cmd);
                   1322:   install_element (RMAP_NODE, &rmap_continue_index_cmd);
                   1323:   
                   1324:   /* Install the call stuff. */
                   1325:   install_element (RMAP_NODE, &rmap_call_cmd);
                   1326:   install_element (RMAP_NODE, &no_rmap_call_cmd);
                   1327: 
                   1328:   /* Install description commands. */
                   1329:   install_element (RMAP_NODE, &rmap_description_cmd);
                   1330:   install_element (RMAP_NODE, &no_rmap_description_cmd);
                   1331:    
                   1332:   /* Install show command */
                   1333:   install_element (ENABLE_NODE, &rmap_show_name_cmd);
                   1334: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>